Split CursorOptions off of Window (#19668)
# Objective - Fixes #19627 - Tackles part of #19644 - Supersedes #19629 - `Window` has become a very very very big component - As such, our change detection does not *really* work on it, as e.g. moving the mouse will cause a change for the entire window - We circumvented this with a cache - But, some things *shouldn't* be cached as they can be changed from outside the user's control, notably the cursor grab mode on web - So, we need to disable the cache for that - But because change detection is broken, that would result in the cursor grab mode being set every frame the mouse is moved - That is usually *not* what a dev wants, as it forces the cursor to be locked even when the end-user is trying to free the cursor on the browser - the cache in this situation is invalid due to #8949 ## Solution - Split `Window` into multiple components, each with working change detection - Disable caching of the cursor grab mode - This will only attempt to force the grab mode when the `CursorOptions` were touched by the user, which is *much* rarer than simply moving the mouse. - If this PR is merged, I'll do the exact same for the other constituents of `Window` as a follow-up ## Testing - Ran all the changed examples
This commit is contained in:
parent
d1c6fbea57
commit
a750cfe4a1
@ -57,6 +57,7 @@ impl Default for WindowPlugin {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
WindowPlugin {
|
WindowPlugin {
|
||||||
primary_window: Some(Window::default()),
|
primary_window: Some(Window::default()),
|
||||||
|
primary_cursor_options: Some(CursorOptions::default()),
|
||||||
exit_condition: ExitCondition::OnAllClosed,
|
exit_condition: ExitCondition::OnAllClosed,
|
||||||
close_when_requested: true,
|
close_when_requested: true,
|
||||||
}
|
}
|
||||||
@ -76,6 +77,13 @@ pub struct WindowPlugin {
|
|||||||
/// [`exit_on_all_closed`].
|
/// [`exit_on_all_closed`].
|
||||||
pub primary_window: Option<Window>,
|
pub primary_window: Option<Window>,
|
||||||
|
|
||||||
|
/// Settings for the cursor on the primary window.
|
||||||
|
///
|
||||||
|
/// Defaults to `Some(CursorOptions::default())`.
|
||||||
|
///
|
||||||
|
/// Has no effect if [`WindowPlugin::primary_window`] is `None`.
|
||||||
|
pub primary_cursor_options: Option<CursorOptions>,
|
||||||
|
|
||||||
/// Whether to exit the app when there are no open windows.
|
/// Whether to exit the app when there are no open windows.
|
||||||
///
|
///
|
||||||
/// If disabling this, ensure that you send the [`bevy_app::AppExit`]
|
/// If disabling this, ensure that you send the [`bevy_app::AppExit`]
|
||||||
@ -122,10 +130,14 @@ impl Plugin for WindowPlugin {
|
|||||||
.add_event::<AppLifecycle>();
|
.add_event::<AppLifecycle>();
|
||||||
|
|
||||||
if let Some(primary_window) = &self.primary_window {
|
if let Some(primary_window) = &self.primary_window {
|
||||||
app.world_mut().spawn(primary_window.clone()).insert((
|
let mut entity_commands = app.world_mut().spawn(primary_window.clone());
|
||||||
|
entity_commands.insert((
|
||||||
PrimaryWindow,
|
PrimaryWindow,
|
||||||
RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
|
RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
|
||||||
));
|
));
|
||||||
|
if let Some(primary_cursor_options) = &self.primary_cursor_options {
|
||||||
|
entity_commands.insert(primary_cursor_options.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.exit_condition {
|
match self.exit_condition {
|
||||||
@ -168,7 +180,8 @@ impl Plugin for WindowPlugin {
|
|||||||
// Register window descriptor and related types
|
// Register window descriptor and related types
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
app.register_type::<Window>()
|
app.register_type::<Window>()
|
||||||
.register_type::<PrimaryWindow>();
|
.register_type::<PrimaryWindow>()
|
||||||
|
.register_type::<CursorOptions>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,10 +158,8 @@ impl ContainsEntity for NormalizedWindowRef {
|
|||||||
all(feature = "serialize", feature = "bevy_reflect"),
|
all(feature = "serialize", feature = "bevy_reflect"),
|
||||||
reflect(Serialize, Deserialize)
|
reflect(Serialize, Deserialize)
|
||||||
)]
|
)]
|
||||||
|
#[require(CursorOptions)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
/// The cursor options of this window. Cursor icons are set with the `Cursor` component on the
|
|
||||||
/// window entity.
|
|
||||||
pub cursor_options: CursorOptions,
|
|
||||||
/// What presentation mode to give the window.
|
/// What presentation mode to give the window.
|
||||||
pub present_mode: PresentMode,
|
pub present_mode: PresentMode,
|
||||||
/// Which fullscreen or windowing mode should be used.
|
/// Which fullscreen or windowing mode should be used.
|
||||||
@ -470,7 +468,6 @@ impl Default for Window {
|
|||||||
Self {
|
Self {
|
||||||
title: DEFAULT_WINDOW_TITLE.to_owned(),
|
title: DEFAULT_WINDOW_TITLE.to_owned(),
|
||||||
name: None,
|
name: None,
|
||||||
cursor_options: Default::default(),
|
|
||||||
present_mode: Default::default(),
|
present_mode: Default::default(),
|
||||||
mode: Default::default(),
|
mode: Default::default(),
|
||||||
position: Default::default(),
|
position: Default::default(),
|
||||||
@ -728,11 +725,11 @@ impl WindowResizeConstraints {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cursor data for a [`Window`].
|
/// Cursor data for a [`Window`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Component, Debug, Clone)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "bevy_reflect",
|
feature = "bevy_reflect",
|
||||||
derive(Reflect),
|
derive(Reflect),
|
||||||
reflect(Debug, Default, Clone)
|
reflect(Component, Debug, Default, Clone)
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
|
@ -25,8 +25,8 @@ use winit::{event_loop::EventLoop, window::WindowId};
|
|||||||
use bevy_a11y::AccessibilityRequested;
|
use bevy_a11y::AccessibilityRequested;
|
||||||
use bevy_app::{App, Last, Plugin};
|
use bevy_app::{App, Last, Plugin};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_window::{exit_on_all_closed, Window, WindowCreated};
|
use bevy_window::{exit_on_all_closed, CursorOptions, Window, WindowCreated};
|
||||||
use system::{changed_windows, check_keyboard_focus_lost, despawn_windows};
|
use system::{changed_cursor_options, changed_windows, check_keyboard_focus_lost, despawn_windows};
|
||||||
pub use system::{create_monitors, create_windows};
|
pub use system::{create_monitors, create_windows};
|
||||||
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
|
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
|
||||||
pub use winit::platform::web::CustomCursorExtWebSys;
|
pub use winit::platform::web::CustomCursorExtWebSys;
|
||||||
@ -142,6 +142,7 @@ impl<T: BufferedEvent> Plugin for WinitPlugin<T> {
|
|||||||
// `exit_on_all_closed` only checks if windows exist but doesn't access data,
|
// `exit_on_all_closed` only checks if windows exist but doesn't access data,
|
||||||
// so we don't need to care about its ordering relative to `changed_windows`
|
// so we don't need to care about its ordering relative to `changed_windows`
|
||||||
changed_windows.ambiguous_with(exit_on_all_closed),
|
changed_windows.ambiguous_with(exit_on_all_closed),
|
||||||
|
changed_cursor_options,
|
||||||
despawn_windows,
|
despawn_windows,
|
||||||
check_keyboard_focus_lost,
|
check_keyboard_focus_lost,
|
||||||
)
|
)
|
||||||
@ -211,6 +212,7 @@ pub type CreateWindowParams<'w, 's, F = ()> = (
|
|||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
&'static mut Window,
|
&'static mut Window,
|
||||||
|
&'static CursorOptions,
|
||||||
Option<&'static RawHandleWrapperHolder>,
|
Option<&'static RawHandleWrapperHolder>,
|
||||||
),
|
),
|
||||||
F,
|
F,
|
||||||
|
@ -46,7 +46,7 @@ use bevy_window::{
|
|||||||
WindowScaleFactorChanged, WindowThemeChanged,
|
WindowScaleFactorChanged, WindowThemeChanged,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use bevy_window::{PrimaryWindow, RawHandleWrapper};
|
use bevy_window::{CursorOptions, PrimaryWindow, RawHandleWrapper};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
accessibility::ACCESS_KIT_ADAPTERS,
|
accessibility::ACCESS_KIT_ADAPTERS,
|
||||||
@ -474,7 +474,7 @@ impl<T: BufferedEvent> ApplicationHandler<T> for WinitAppRunnerState<T> {
|
|||||||
if let Ok((window_component, mut cache)) = windows.get_mut(self.world_mut(), window)
|
if let Ok((window_component, mut cache)) = windows.get_mut(self.world_mut(), window)
|
||||||
{
|
{
|
||||||
if window_component.is_changed() {
|
if window_component.is_changed() {
|
||||||
cache.window = window_component.clone();
|
**cache = window_component.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -605,10 +605,12 @@ impl<T: BufferedEvent> WinitAppRunnerState<T> {
|
|||||||
{
|
{
|
||||||
// Get windows that are cached but without raw handles. Those window were already created, but got their
|
// Get windows that are cached but without raw handles. Those window were already created, but got their
|
||||||
// handle wrapper removed when the app was suspended.
|
// handle wrapper removed when the app was suspended.
|
||||||
|
|
||||||
let mut query = self.world_mut()
|
let mut query = self.world_mut()
|
||||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<RawHandleWrapper>)>();
|
.query_filtered::<(Entity, &Window, &CursorOptions), (With<CachedWindow>, Without<RawHandleWrapper>)>();
|
||||||
if let Ok((entity, window)) = query.single(&self.world()) {
|
if let Ok((entity, window, cursor_options)) = query.single(&self.world()) {
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
|
let cursor_options = cursor_options.clone();
|
||||||
|
|
||||||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
||||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||||
@ -622,6 +624,7 @@ impl<T: BufferedEvent> WinitAppRunnerState<T> {
|
|||||||
event_loop,
|
event_loop,
|
||||||
entity,
|
entity,
|
||||||
&window,
|
&window,
|
||||||
|
&cursor_options,
|
||||||
adapters,
|
adapters,
|
||||||
&mut handlers,
|
&mut handlers,
|
||||||
&accessibility_requested,
|
&accessibility_requested,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use bevy_derive::{Deref, DerefMut};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
change_detection::DetectChangesMut,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::EventWriter,
|
event::EventWriter,
|
||||||
lifecycle::RemovedComponents,
|
lifecycle::RemovedComponents,
|
||||||
@ -10,9 +12,9 @@ use bevy_ecs::{
|
|||||||
};
|
};
|
||||||
use bevy_input::keyboard::{Key, KeyCode, KeyboardFocusLost, KeyboardInput};
|
use bevy_input::keyboard::{Key, KeyCode, KeyboardFocusLost, KeyboardInput};
|
||||||
use bevy_window::{
|
use bevy_window::{
|
||||||
ClosingWindow, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window, WindowClosed,
|
ClosingWindow, CursorOptions, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window,
|
||||||
WindowClosing, WindowCreated, WindowEvent, WindowFocused, WindowMode, WindowResized,
|
WindowClosed, WindowClosing, WindowCreated, WindowEvent, WindowFocused, WindowMode,
|
||||||
WindowWrapper,
|
WindowResized, WindowWrapper,
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ pub fn create_windows<F: QueryFilter + 'static>(
|
|||||||
) {
|
) {
|
||||||
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
WINIT_WINDOWS.with_borrow_mut(|winit_windows| {
|
||||||
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
ACCESS_KIT_ADAPTERS.with_borrow_mut(|adapters| {
|
||||||
for (entity, mut window, handle_holder) in &mut created_windows {
|
for (entity, mut window, cursor_options, handle_holder) in &mut created_windows {
|
||||||
if winit_windows.get_window(entity).is_some() {
|
if winit_windows.get_window(entity).is_some() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -70,6 +72,7 @@ pub fn create_windows<F: QueryFilter + 'static>(
|
|||||||
event_loop,
|
event_loop,
|
||||||
entity,
|
entity,
|
||||||
&window,
|
&window,
|
||||||
|
cursor_options,
|
||||||
adapters,
|
adapters,
|
||||||
&mut handlers,
|
&mut handlers,
|
||||||
&accessibility_requested,
|
&accessibility_requested,
|
||||||
@ -85,9 +88,8 @@ pub fn create_windows<F: QueryFilter + 'static>(
|
|||||||
.set_scale_factor_and_apply_to_physical_size(winit_window.scale_factor() as f32);
|
.set_scale_factor_and_apply_to_physical_size(winit_window.scale_factor() as f32);
|
||||||
|
|
||||||
commands.entity(entity).insert((
|
commands.entity(entity).insert((
|
||||||
CachedWindow {
|
CachedWindow(window.clone()),
|
||||||
window: window.clone(),
|
CachedCursorOptions(cursor_options.clone()),
|
||||||
},
|
|
||||||
WinitWindowPressedKeys::default(),
|
WinitWindowPressedKeys::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -281,10 +283,12 @@ pub(crate) fn despawn_windows(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The cached state of the window so we can check which properties were changed from within the app.
|
/// The cached state of the window so we can check which properties were changed from within the app.
|
||||||
#[derive(Debug, Clone, Component)]
|
#[derive(Debug, Clone, Component, Deref, DerefMut)]
|
||||||
pub struct CachedWindow {
|
pub(crate) struct CachedWindow(Window);
|
||||||
pub window: Window,
|
|
||||||
}
|
/// The cached state of the window so we can check which properties were changed from within the app.
|
||||||
|
#[derive(Debug, Clone, Component, Deref, DerefMut)]
|
||||||
|
pub(crate) struct CachedCursorOptions(CursorOptions);
|
||||||
|
|
||||||
/// Propagates changes from [`Window`] entities to the [`winit`] backend.
|
/// Propagates changes from [`Window`] entities to the [`winit`] backend.
|
||||||
///
|
///
|
||||||
@ -306,11 +310,11 @@ pub(crate) fn changed_windows(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if window.title != cache.window.title {
|
if window.title != cache.title {
|
||||||
winit_window.set_title(window.title.as_str());
|
winit_window.set_title(window.title.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.mode != cache.window.mode {
|
if window.mode != cache.mode {
|
||||||
let new_mode = match window.mode {
|
let new_mode = match window.mode {
|
||||||
WindowMode::BorderlessFullscreen(monitor_selection) => {
|
WindowMode::BorderlessFullscreen(monitor_selection) => {
|
||||||
Some(Some(winit::window::Fullscreen::Borderless(select_monitor(
|
Some(Some(winit::window::Fullscreen::Borderless(select_monitor(
|
||||||
@ -352,15 +356,15 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.resolution != cache.window.resolution {
|
if window.resolution != cache.resolution {
|
||||||
let mut physical_size = PhysicalSize::new(
|
let mut physical_size = PhysicalSize::new(
|
||||||
window.resolution.physical_width(),
|
window.resolution.physical_width(),
|
||||||
window.resolution.physical_height(),
|
window.resolution.physical_height(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let cached_physical_size = PhysicalSize::new(
|
let cached_physical_size = PhysicalSize::new(
|
||||||
cache.window.physical_width(),
|
cache.physical_width(),
|
||||||
cache.window.physical_height(),
|
cache.physical_height(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let base_scale_factor = window.resolution.base_scale_factor();
|
let base_scale_factor = window.resolution.base_scale_factor();
|
||||||
@ -368,12 +372,12 @@ pub(crate) fn changed_windows(
|
|||||||
// Note: this may be different from `winit`'s base scale factor if
|
// Note: this may be different from `winit`'s base scale factor if
|
||||||
// `scale_factor_override` is set to Some(f32)
|
// `scale_factor_override` is set to Some(f32)
|
||||||
let scale_factor = window.scale_factor();
|
let scale_factor = window.scale_factor();
|
||||||
let cached_scale_factor = cache.window.scale_factor();
|
let cached_scale_factor = cache.scale_factor();
|
||||||
|
|
||||||
// Check and update `winit`'s physical size only if the window is not maximized
|
// Check and update `winit`'s physical size only if the window is not maximized
|
||||||
if scale_factor != cached_scale_factor && !winit_window.is_maximized() {
|
if scale_factor != cached_scale_factor && !winit_window.is_maximized() {
|
||||||
let logical_size =
|
let logical_size =
|
||||||
if let Some(cached_factor) = cache.window.resolution.scale_factor_override() {
|
if let Some(cached_factor) = cache.resolution.scale_factor_override() {
|
||||||
physical_size.to_logical::<f32>(cached_factor as f64)
|
physical_size.to_logical::<f32>(cached_factor as f64)
|
||||||
} else {
|
} else {
|
||||||
physical_size.to_logical::<f32>(base_scale_factor as f64)
|
physical_size.to_logical::<f32>(base_scale_factor as f64)
|
||||||
@ -397,7 +401,7 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.physical_cursor_position() != cache.window.physical_cursor_position() {
|
if window.physical_cursor_position() != cache.physical_cursor_position() {
|
||||||
if let Some(physical_position) = window.physical_cursor_position() {
|
if let Some(physical_position) = window.physical_cursor_position() {
|
||||||
let position = PhysicalPosition::new(physical_position.x, physical_position.y);
|
let position = PhysicalPosition::new(physical_position.x, physical_position.y);
|
||||||
|
|
||||||
@ -407,44 +411,23 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode
|
if window.decorations != cache.decorations
|
||||||
&& crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
window.cursor_options.grab_mode = cache.window.cursor_options.grab_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if window.cursor_options.visible != cache.window.cursor_options.visible {
|
|
||||||
winit_window.set_cursor_visible(window.cursor_options.visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
if window.cursor_options.hit_test != cache.window.cursor_options.hit_test {
|
|
||||||
if let Err(err) = winit_window.set_cursor_hittest(window.cursor_options.hit_test) {
|
|
||||||
window.cursor_options.hit_test = cache.window.cursor_options.hit_test;
|
|
||||||
warn!(
|
|
||||||
"Could not set cursor hit test for window {}: {}",
|
|
||||||
window.title, err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if window.decorations != cache.window.decorations
|
|
||||||
&& window.decorations != winit_window.is_decorated()
|
&& window.decorations != winit_window.is_decorated()
|
||||||
{
|
{
|
||||||
winit_window.set_decorations(window.decorations);
|
winit_window.set_decorations(window.decorations);
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.resizable != cache.window.resizable
|
if window.resizable != cache.resizable
|
||||||
&& window.resizable != winit_window.is_resizable()
|
&& window.resizable != winit_window.is_resizable()
|
||||||
{
|
{
|
||||||
winit_window.set_resizable(window.resizable);
|
winit_window.set_resizable(window.resizable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.enabled_buttons != cache.window.enabled_buttons {
|
if window.enabled_buttons != cache.enabled_buttons {
|
||||||
winit_window.set_enabled_buttons(convert_enabled_buttons(window.enabled_buttons));
|
winit_window.set_enabled_buttons(convert_enabled_buttons(window.enabled_buttons));
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.resize_constraints != cache.window.resize_constraints {
|
if window.resize_constraints != cache.resize_constraints {
|
||||||
let constraints = window.resize_constraints.check_constraints();
|
let constraints = window.resize_constraints.check_constraints();
|
||||||
let min_inner_size = LogicalSize {
|
let min_inner_size = LogicalSize {
|
||||||
width: constraints.min_width,
|
width: constraints.min_width,
|
||||||
@ -461,7 +444,7 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.position != cache.window.position {
|
if window.position != cache.position {
|
||||||
if let Some(position) = crate::winit_window_position(
|
if let Some(position) = crate::winit_window_position(
|
||||||
&window.position,
|
&window.position,
|
||||||
&window.resolution,
|
&window.resolution,
|
||||||
@ -502,62 +485,62 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.focused != cache.window.focused && window.focused {
|
if window.focused != cache.focused && window.focused {
|
||||||
winit_window.focus_window();
|
winit_window.focus_window();
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.window_level != cache.window.window_level {
|
if window.window_level != cache.window_level {
|
||||||
winit_window.set_window_level(convert_window_level(window.window_level));
|
winit_window.set_window_level(convert_window_level(window.window_level));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently unsupported changes
|
// Currently unsupported changes
|
||||||
if window.transparent != cache.window.transparent {
|
if window.transparent != cache.transparent {
|
||||||
window.transparent = cache.window.transparent;
|
window.transparent = cache.transparent;
|
||||||
warn!("Winit does not currently support updating transparency after window creation.");
|
warn!("Winit does not currently support updating transparency after window creation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
if window.canvas != cache.window.canvas {
|
if window.canvas != cache.canvas {
|
||||||
window.canvas.clone_from(&cache.window.canvas);
|
window.canvas.clone_from(&cache.canvas);
|
||||||
warn!(
|
warn!(
|
||||||
"Bevy currently doesn't support modifying the window canvas after initialization."
|
"Bevy currently doesn't support modifying the window canvas after initialization."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.ime_enabled != cache.window.ime_enabled {
|
if window.ime_enabled != cache.ime_enabled {
|
||||||
winit_window.set_ime_allowed(window.ime_enabled);
|
winit_window.set_ime_allowed(window.ime_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.ime_position != cache.window.ime_position {
|
if window.ime_position != cache.ime_position {
|
||||||
winit_window.set_ime_cursor_area(
|
winit_window.set_ime_cursor_area(
|
||||||
LogicalPosition::new(window.ime_position.x, window.ime_position.y),
|
LogicalPosition::new(window.ime_position.x, window.ime_position.y),
|
||||||
PhysicalSize::new(10, 10),
|
PhysicalSize::new(10, 10),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.window_theme != cache.window.window_theme {
|
if window.window_theme != cache.window_theme {
|
||||||
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
winit_window.set_theme(window.window_theme.map(convert_window_theme));
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.visible != cache.window.visible {
|
if window.visible != cache.visible {
|
||||||
winit_window.set_visible(window.visible);
|
winit_window.set_visible(window.visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "ios")]
|
#[cfg(target_os = "ios")]
|
||||||
{
|
{
|
||||||
if window.recognize_pinch_gesture != cache.window.recognize_pinch_gesture {
|
if window.recognize_pinch_gesture != cache.recognize_pinch_gesture {
|
||||||
winit_window.recognize_pinch_gesture(window.recognize_pinch_gesture);
|
winit_window.recognize_pinch_gesture(window.recognize_pinch_gesture);
|
||||||
}
|
}
|
||||||
if window.recognize_rotation_gesture != cache.window.recognize_rotation_gesture {
|
if window.recognize_rotation_gesture != cache.recognize_rotation_gesture {
|
||||||
winit_window.recognize_rotation_gesture(window.recognize_rotation_gesture);
|
winit_window.recognize_rotation_gesture(window.recognize_rotation_gesture);
|
||||||
}
|
}
|
||||||
if window.recognize_doubletap_gesture != cache.window.recognize_doubletap_gesture {
|
if window.recognize_doubletap_gesture != cache.recognize_doubletap_gesture {
|
||||||
winit_window.recognize_doubletap_gesture(window.recognize_doubletap_gesture);
|
winit_window.recognize_doubletap_gesture(window.recognize_doubletap_gesture);
|
||||||
}
|
}
|
||||||
if window.recognize_pan_gesture != cache.window.recognize_pan_gesture {
|
if window.recognize_pan_gesture != cache.recognize_pan_gesture {
|
||||||
match (
|
match (
|
||||||
window.recognize_pan_gesture,
|
window.recognize_pan_gesture,
|
||||||
cache.window.recognize_pan_gesture,
|
cache.recognize_pan_gesture,
|
||||||
) {
|
) {
|
||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
warn!("Bevy currently doesn't support modifying PanGesture number of fingers recognition. Please disable it before re-enabling it with the new number of fingers");
|
warn!("Bevy currently doesn't support modifying PanGesture number of fingers recognition. Please disable it before re-enabling it with the new number of fingers");
|
||||||
@ -567,16 +550,15 @@ pub(crate) fn changed_windows(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if window.prefers_home_indicator_hidden != cache.window.prefers_home_indicator_hidden {
|
if window.prefers_home_indicator_hidden != cache.prefers_home_indicator_hidden {
|
||||||
winit_window
|
winit_window
|
||||||
.set_prefers_home_indicator_hidden(window.prefers_home_indicator_hidden);
|
.set_prefers_home_indicator_hidden(window.prefers_home_indicator_hidden);
|
||||||
}
|
}
|
||||||
if window.prefers_status_bar_hidden != cache.window.prefers_status_bar_hidden {
|
if window.prefers_status_bar_hidden != cache.prefers_status_bar_hidden {
|
||||||
winit_window.set_prefers_status_bar_hidden(window.prefers_status_bar_hidden);
|
winit_window.set_prefers_status_bar_hidden(window.prefers_status_bar_hidden);
|
||||||
}
|
}
|
||||||
if window.preferred_screen_edges_deferring_system_gestures
|
if window.preferred_screen_edges_deferring_system_gestures
|
||||||
!= cache
|
!= cache
|
||||||
.window
|
|
||||||
.preferred_screen_edges_deferring_system_gestures
|
.preferred_screen_edges_deferring_system_gestures
|
||||||
{
|
{
|
||||||
use crate::converters::convert_screen_edge;
|
use crate::converters::convert_screen_edge;
|
||||||
@ -585,7 +567,59 @@ pub(crate) fn changed_windows(
|
|||||||
winit_window.set_preferred_screen_edges_deferring_system_gestures(preferred_edge);
|
winit_window.set_preferred_screen_edges_deferring_system_gestures(preferred_edge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache.window = window.clone();
|
**cache = window.clone();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn changed_cursor_options(
|
||||||
|
mut changed_windows: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
|
&Window,
|
||||||
|
&mut CursorOptions,
|
||||||
|
&mut CachedCursorOptions,
|
||||||
|
),
|
||||||
|
Changed<CursorOptions>,
|
||||||
|
>,
|
||||||
|
_non_send_marker: NonSendMarker,
|
||||||
|
) {
|
||||||
|
WINIT_WINDOWS.with_borrow(|winit_windows| {
|
||||||
|
for (entity, window, mut cursor_options, mut cache) in &mut changed_windows {
|
||||||
|
// This system already only runs when the cursor options change, so we need to bypass change detection or the next frame will also run this system
|
||||||
|
let cursor_options = cursor_options.bypass_change_detection();
|
||||||
|
let Some(winit_window) = winit_windows.get_window(entity) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
// Don't check the cache for the grab mode. It can change through external means, leaving the cache outdated.
|
||||||
|
if let Err(err) =
|
||||||
|
crate::winit_windows::attempt_grab(winit_window, cursor_options.grab_mode)
|
||||||
|
{
|
||||||
|
warn!(
|
||||||
|
"Could not set cursor grab mode for window {}: {}",
|
||||||
|
window.title, err
|
||||||
|
);
|
||||||
|
cursor_options.grab_mode = cache.grab_mode;
|
||||||
|
} else {
|
||||||
|
cache.grab_mode = cursor_options.grab_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor_options.visible != cache.visible {
|
||||||
|
winit_window.set_cursor_visible(cursor_options.visible);
|
||||||
|
cache.visible = cursor_options.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cursor_options.hit_test != cache.hit_test {
|
||||||
|
if let Err(err) = winit_window.set_cursor_hittest(cursor_options.hit_test) {
|
||||||
|
warn!(
|
||||||
|
"Could not set cursor hit test for window {}: {}",
|
||||||
|
window.title, err
|
||||||
|
);
|
||||||
|
cursor_options.hit_test = cache.hit_test;
|
||||||
|
} else {
|
||||||
|
cache.hit_test = cursor_options.hit_test;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use bevy_ecs::entity::Entity;
|
|||||||
use bevy_ecs::entity::EntityHashMap;
|
use bevy_ecs::entity::EntityHashMap;
|
||||||
use bevy_platform::collections::HashMap;
|
use bevy_platform::collections::HashMap;
|
||||||
use bevy_window::{
|
use bevy_window::{
|
||||||
CursorGrabMode, MonitorSelection, VideoModeSelection, Window, WindowMode, WindowPosition,
|
CursorGrabMode, CursorOptions, MonitorSelection, VideoModeSelection, Window, WindowMode,
|
||||||
WindowResolution, WindowWrapper,
|
WindowPosition, WindowResolution, WindowWrapper,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ impl WinitWindows {
|
|||||||
event_loop: &ActiveEventLoop,
|
event_loop: &ActiveEventLoop,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
|
cursor_options: &CursorOptions,
|
||||||
adapters: &mut AccessKitAdapters,
|
adapters: &mut AccessKitAdapters,
|
||||||
handlers: &mut WinitActionRequestHandlers,
|
handlers: &mut WinitActionRequestHandlers,
|
||||||
accessibility_requested: &AccessibilityRequested,
|
accessibility_requested: &AccessibilityRequested,
|
||||||
@ -310,16 +311,16 @@ impl WinitWindows {
|
|||||||
winit_window.set_visible(window.visible);
|
winit_window.set_visible(window.visible);
|
||||||
|
|
||||||
// Do not set the grab mode on window creation if it's none. It can fail on mobile.
|
// Do not set the grab mode on window creation if it's none. It can fail on mobile.
|
||||||
if window.cursor_options.grab_mode != CursorGrabMode::None {
|
if cursor_options.grab_mode != CursorGrabMode::None {
|
||||||
let _ = attempt_grab(&winit_window, window.cursor_options.grab_mode);
|
let _ = attempt_grab(&winit_window, cursor_options.grab_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
winit_window.set_cursor_visible(window.cursor_options.visible);
|
winit_window.set_cursor_visible(cursor_options.visible);
|
||||||
|
|
||||||
// Do not set the cursor hittest on window creation if it's false, as it will always fail on
|
// Do not set the cursor hittest on window creation if it's false, as it will always fail on
|
||||||
// some platforms and log an unfixable warning.
|
// some platforms and log an unfixable warning.
|
||||||
if !window.cursor_options.hit_test {
|
if !cursor_options.hit_test {
|
||||||
if let Err(err) = winit_window.set_cursor_hittest(window.cursor_options.hit_test) {
|
if let Err(err) = winit_window.set_cursor_hittest(cursor_options.hit_test) {
|
||||||
warn!(
|
warn!(
|
||||||
"Could not set cursor hit test for window {}: {}",
|
"Could not set cursor hit test for window {}: {}",
|
||||||
window.title, err
|
window.title, err
|
||||||
|
@ -10,7 +10,7 @@ use bevy::{
|
|||||||
app::AppExit,
|
app::AppExit,
|
||||||
input::common_conditions::{input_just_pressed, input_just_released},
|
input::common_conditions::{input_just_pressed, input_just_released},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::{PrimaryWindow, WindowLevel},
|
window::{CursorOptions, PrimaryWindow, WindowLevel},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -219,12 +219,13 @@ fn get_cursor_world_pos(
|
|||||||
/// Update whether the window is clickable or not
|
/// Update whether the window is clickable or not
|
||||||
fn update_cursor_hit_test(
|
fn update_cursor_hit_test(
|
||||||
cursor_world_pos: Res<CursorWorldPos>,
|
cursor_world_pos: Res<CursorWorldPos>,
|
||||||
mut primary_window: Single<&mut Window, With<PrimaryWindow>>,
|
primary_window: Single<(&Window, &mut CursorOptions), With<PrimaryWindow>>,
|
||||||
bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
|
bevy_logo_transform: Single<&Transform, With<BevyLogo>>,
|
||||||
) {
|
) {
|
||||||
|
let (window, mut cursor_options) = primary_window.into_inner();
|
||||||
// If the window has decorations (e.g. a border) then it should be clickable
|
// If the window has decorations (e.g. a border) then it should be clickable
|
||||||
if primary_window.decorations {
|
if window.decorations {
|
||||||
primary_window.cursor_options.hit_test = true;
|
cursor_options.hit_test = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +235,7 @@ fn update_cursor_hit_test(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If the cursor is within the radius of the Bevy logo make the window clickable otherwise the window is not clickable
|
// If the cursor is within the radius of the Bevy logo make the window clickable otherwise the window is not clickable
|
||||||
primary_window.cursor_options.hit_test = bevy_logo_transform
|
cursor_options.hit_test = bevy_logo_transform
|
||||||
.translation
|
.translation
|
||||||
.truncate()
|
.truncate()
|
||||||
.distance(cursor_world_pos)
|
.distance(cursor_world_pos)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit},
|
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::CursorGrabMode,
|
window::{CursorGrabMode, CursorOptions},
|
||||||
};
|
};
|
||||||
use std::{f32::consts::*, fmt};
|
use std::{f32::consts::*, fmt};
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ Freecam Controls:
|
|||||||
|
|
||||||
fn run_camera_controller(
|
fn run_camera_controller(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut windows: Query<&mut Window>,
|
mut windows: Query<(&Window, &mut CursorOptions)>,
|
||||||
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
|
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
|
||||||
accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
|
accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
|
||||||
mouse_button_input: Res<ButtonInput<MouseButton>>,
|
mouse_button_input: Res<ButtonInput<MouseButton>>,
|
||||||
@ -226,18 +226,18 @@ fn run_camera_controller(
|
|||||||
// Handle cursor grab
|
// Handle cursor grab
|
||||||
if cursor_grab_change {
|
if cursor_grab_change {
|
||||||
if cursor_grab {
|
if cursor_grab {
|
||||||
for mut window in &mut windows {
|
for (window, mut cursor_options) in &mut windows {
|
||||||
if !window.focused {
|
if !window.focused {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||||
window.cursor_options.visible = false;
|
cursor_options.visible = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for mut window in &mut windows {
|
for (_, mut cursor_options) in &mut windows {
|
||||||
window.cursor_options.grab_mode = CursorGrabMode::None;
|
cursor_options.grab_mode = CursorGrabMode::None;
|
||||||
window.cursor_options.visible = true;
|
cursor_options.visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
//! Demonstrates how to grab and hide the mouse cursor.
|
//! Demonstrates how to grab and hide the mouse cursor.
|
||||||
|
|
||||||
use bevy::{prelude::*, window::CursorGrabMode};
|
use bevy::{
|
||||||
|
prelude::*,
|
||||||
|
window::{CursorGrabMode, CursorOptions},
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@ -12,17 +15,17 @@ fn main() {
|
|||||||
// This system grabs the mouse when the left mouse button is pressed
|
// This system grabs the mouse when the left mouse button is pressed
|
||||||
// and releases it when the escape key is pressed
|
// and releases it when the escape key is pressed
|
||||||
fn grab_mouse(
|
fn grab_mouse(
|
||||||
mut window: Single<&mut Window>,
|
mut cursor_options: Single<&mut CursorOptions>,
|
||||||
mouse: Res<ButtonInput<MouseButton>>,
|
mouse: Res<ButtonInput<MouseButton>>,
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
) {
|
) {
|
||||||
if mouse.just_pressed(MouseButton::Left) {
|
if mouse.just_pressed(MouseButton::Left) {
|
||||||
window.cursor_options.visible = false;
|
cursor_options.visible = false;
|
||||||
window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.just_pressed(KeyCode::Escape) {
|
if key.just_pressed(KeyCode::Escape) {
|
||||||
window.cursor_options.visible = true;
|
cursor_options.visible = true;
|
||||||
window.cursor_options.grab_mode = CursorGrabMode::None;
|
cursor_options.grab_mode = CursorGrabMode::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//! If you build this, and hit 'P' it should toggle on/off the mouse's passthrough.
|
//! If you build this, and hit 'P' it should toggle on/off the mouse's passthrough.
|
||||||
//! Note: this example will not work on following platforms: iOS / Android / Web / X11. Window fall through is not supported there.
|
//! Note: this example will not work on following platforms: iOS / Android / Web / X11. Window fall through is not supported there.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{prelude::*, window::CursorOptions};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@ -46,9 +46,9 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||||||
// A simple system to handle some keyboard input and toggle on/off the hit test.
|
// A simple system to handle some keyboard input and toggle on/off the hit test.
|
||||||
fn toggle_mouse_passthrough(
|
fn toggle_mouse_passthrough(
|
||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut window: Single<&mut Window>,
|
mut cursor_options: Single<&mut CursorOptions>,
|
||||||
) {
|
) {
|
||||||
if keyboard_input.just_pressed(KeyCode::KeyP) {
|
if keyboard_input.just_pressed(KeyCode::KeyP) {
|
||||||
window.cursor_options.hit_test = !window.cursor_options.hit_test;
|
cursor_options.hit_test = !cursor_options.hit_test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ use bevy::winit::cursor::{CustomCursor, CustomCursorImage};
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
diagnostic::{FrameCount, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
diagnostic::{FrameCount, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::{CursorGrabMode, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme},
|
window::{
|
||||||
|
CursorGrabMode, CursorOptions, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme,
|
||||||
|
},
|
||||||
winit::cursor::CursorIcon,
|
winit::cursor::CursorIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,10 +130,10 @@ fn change_title(mut window: Single<&mut Window>, time: Res<Time>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_cursor(mut window: Single<&mut Window>, input: Res<ButtonInput<KeyCode>>) {
|
fn toggle_cursor(mut cursor_options: Single<&mut CursorOptions>, input: Res<ButtonInput<KeyCode>>) {
|
||||||
if input.just_pressed(KeyCode::Space) {
|
if input.just_pressed(KeyCode::Space) {
|
||||||
window.cursor_options.visible = !window.cursor_options.visible;
|
cursor_options.visible = !cursor_options.visible;
|
||||||
window.cursor_options.grab_mode = match window.cursor_options.grab_mode {
|
cursor_options.grab_mode = match cursor_options.grab_mode {
|
||||||
CursorGrabMode::None => CursorGrabMode::Locked,
|
CursorGrabMode::None => CursorGrabMode::Locked,
|
||||||
CursorGrabMode::Locked | CursorGrabMode::Confined => CursorGrabMode::None,
|
CursorGrabMode::Locked | CursorGrabMode::Confined => CursorGrabMode::None,
|
||||||
};
|
};
|
||||||
|
44
release-content/migration-guides/split-window.md
Normal file
44
release-content/migration-guides/split-window.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
title: Window is now split into multiple components
|
||||||
|
pull_requests: [19668]
|
||||||
|
---
|
||||||
|
|
||||||
|
`Window` has become a very large component over the last few releases. To improve our internal handling of it and to make it more approachable, we
|
||||||
|
have split it into multiple components, all on the same entity. So far, this affects `CursorOptions`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// old
|
||||||
|
fn lock_cursor(primary_window: Single<&mut Window, With<PrimaryWindow>>) {
|
||||||
|
primary_window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// new
|
||||||
|
fn lock_cursor(primary_cursor_options: Single<&mut CursorOptions, With<PrimaryWindow>>) {
|
||||||
|
primary_cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This split also applies when specifying the initial settings for the primary window:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// old
|
||||||
|
app.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
cursor_options: CursorOptions {
|
||||||
|
grab_mode: CursorGrabMode::Locked,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// new
|
||||||
|
app.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_cursor_options: Some(CursorOptions {
|
||||||
|
grab_mode: CursorGrabMode::Locked,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}));
|
||||||
|
```
|
@ -3,7 +3,7 @@ index f578658cd..ffac22062 100644
|
|||||||
--- a/crates/bevy_window/src/window.rs
|
--- a/crates/bevy_window/src/window.rs
|
||||||
+++ b/crates/bevy_window/src/window.rs
|
+++ b/crates/bevy_window/src/window.rs
|
||||||
@@ -318,7 +318,7 @@ impl Default for Window {
|
@@ -318,7 +318,7 @@ impl Default for Window {
|
||||||
cursor_options: Default::default(),
|
name: None,
|
||||||
present_mode: Default::default(),
|
present_mode: Default::default(),
|
||||||
mode: Default::default(),
|
mode: Default::default(),
|
||||||
- position: Default::default(),
|
- position: Default::default(),
|
||||||
|
Loading…
Reference in New Issue
Block a user