From 3e5ff598e48bb5e22bc2a6bd10d33c227c242ba9 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:03:25 +0200 Subject: [PATCH 01/32] Replacing `Window_id` with `Entity` --- crates/bevy_window/src/event.rs | 36 +++++++++--------- crates/bevy_winit/src/winit_windows.rs | 51 ++++++++++++++------------ examples/window/multiple_windows.rs | 3 +- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/crates/bevy_window/src/event.rs b/crates/bevy_window/src/event.rs index 8452c482035ca..b1387136b7069 100644 --- a/crates/bevy_window/src/event.rs +++ b/crates/bevy_window/src/event.rs @@ -1,12 +1,13 @@ use std::path::PathBuf; -use super::{WindowDescriptor, WindowId}; +use super::WindowDescriptor; +use bevy_ecs::entity::Entity; use bevy_math::{IVec2, Vec2}; /// A window event that is sent whenever a windows logical size has changed #[derive(Debug, Clone)] pub struct WindowResized { - pub id: WindowId, + pub entity: Entity, /// The new logical width of the window pub width: f32, /// The new logical height of the window @@ -16,10 +17,11 @@ pub struct WindowResized { /// An event that indicates that a new window should be created. #[derive(Debug, Clone)] pub struct CreateWindow { - pub id: WindowId, + pub entity: Entity, pub descriptor: WindowDescriptor, } +// TODO: This would redraw all windows ? If yes, update docs to reflect this /// An event that indicates the window should redraw, even if its control flow is set to `Wait` and /// there have been no window events. #[derive(Debug, Clone)] @@ -31,7 +33,7 @@ pub struct RequestRedraw; /// event will be sent in the handler for that event. #[derive(Debug, Clone)] pub struct WindowCreated { - pub id: WindowId, + pub entity: Entity, } /// An event that is sent whenever the operating systems requests that a window @@ -47,7 +49,7 @@ pub struct WindowCreated { /// [closing]: crate::Window::close #[derive(Debug, Clone)] pub struct WindowCloseRequested { - pub id: WindowId, + pub entity: Entity, } /// An event that is sent whenever a window is closed. This will be sent by the @@ -56,65 +58,65 @@ pub struct WindowCloseRequested { /// [`Window::close`]: crate::Window::close #[derive(Debug, Clone)] pub struct WindowClosed { - pub id: WindowId, + pub entity: Entity, } #[derive(Debug, Clone)] pub struct CursorMoved { - pub id: WindowId, + pub entity: Entity, pub position: Vec2, } #[derive(Debug, Clone)] pub struct CursorEntered { - pub id: WindowId, + pub entity: Entity, } #[derive(Debug, Clone)] pub struct CursorLeft { - pub id: WindowId, + pub entity: Entity, } /// An event that is sent whenever a window receives a character from the OS or underlying system. #[derive(Debug, Clone)] pub struct ReceivedCharacter { - pub id: WindowId, + pub entity: Entity, pub char: char, } /// An event that indicates a window has received or lost focus. #[derive(Debug, Clone)] pub struct WindowFocused { - pub id: WindowId, + pub entity: Entity, pub focused: bool, } /// An event that indicates a window's scale factor has changed. #[derive(Debug, Clone)] pub struct WindowScaleFactorChanged { - pub id: WindowId, + pub entity: Entity, pub scale_factor: f64, } /// An event that indicates a window's OS-reported scale factor has changed. #[derive(Debug, Clone)] pub struct WindowBackendScaleFactorChanged { - pub id: WindowId, + pub entity: Entity, pub scale_factor: f64, } /// Events related to files being dragged and dropped on a window. #[derive(Debug, Clone)] pub enum FileDragAndDrop { - DroppedFile { id: WindowId, path_buf: PathBuf }, + DroppedFile { entity: Entity, path_buf: PathBuf }, - HoveredFile { id: WindowId, path_buf: PathBuf }, + HoveredFile { entity: Entity, path_buf: PathBuf }, - HoveredFileCancelled { id: WindowId }, + HoveredFileCancelled { entity: Entity }, } /// An event that is sent when a window is repositioned in physical pixels. #[derive(Debug, Clone)] pub struct WindowMoved { - pub id: WindowId, + pub entity: Entity, pub position: IVec2, } diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index e5565092701b7..652b07c85fb37 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,14 +1,15 @@ +use bevy_ecs::entity::Entity; use bevy_math::IVec2; use bevy_utils::HashMap; -use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; +use bevy_window::{WindowDescriptor, WindowMode}; use raw_window_handle::HasRawWindowHandle; use winit::dpi::LogicalSize; #[derive(Debug, Default)] pub struct WinitWindows { pub windows: HashMap, - pub window_id_to_winit: HashMap, - pub winit_to_window_id: HashMap, + pub window_id_to_winit: HashMap, + pub winit_to_window_id: HashMap, // Some winit functions, such as `set_window_icon` can only be used from the main thread. If // they are used in another thread, the app will hang. This marker ensures `WinitWindows` is // only ever accessed with bevy's non-send functions and in NonSend systems. @@ -19,9 +20,9 @@ impl WinitWindows { pub fn create_window( &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, - window_id: WindowId, + entity: Entity, window_descriptor: &WindowDescriptor, - ) -> Window { + ) -> winit::window::Window { let mut winit_window_builder = winit::window::WindowBuilder::new(); winit_window_builder = match window_descriptor.mode { @@ -133,8 +134,8 @@ impl WinitWindows { winit_window.set_cursor_visible(window_descriptor.cursor_visible); - self.window_id_to_winit.insert(window_id, winit_window.id()); - self.winit_to_window_id.insert(winit_window.id(), window_id); + self.window_id_to_winit.insert(entity, winit_window.id()); + self.winit_to_window_id.insert(winit_window.id(), entity); #[cfg(target_arch = "wasm32")] { @@ -160,29 +161,33 @@ impl WinitWindows { let scale_factor = winit_window.scale_factor(); let raw_window_handle = winit_window.raw_window_handle(); self.windows.insert(winit_window.id(), winit_window); - Window::new( - window_id, - window_descriptor, - inner_size.width, - inner_size.height, - scale_factor, - position, - raw_window_handle, - ) + + winit_window + // TODO: This should happen through commands + // Window::new( + // entity, + // window_descriptor, + // inner_size.width, + // inner_size.height, + // scale_factor, + // position, + // raw_window_handle, + // ) } - pub fn get_window(&self, id: WindowId) -> Option<&winit::window::Window> { + // TODO: This might not be as useful anymore? If this is a marker component? + pub fn get_window(&self, entity: Entity) -> Option<&winit::window::Window> { self.window_id_to_winit - .get(&id) - .and_then(|id| self.windows.get(id)) + .get(&entity) + .and_then(|winit_id| self.windows.get(winit_id)) } - pub fn get_window_id(&self, id: winit::window::WindowId) -> Option { - self.winit_to_window_id.get(&id).cloned() + pub fn get_window_entity(&self, winit_id: winit::window::WindowId) -> Option { + self.winit_to_window_id.get(&winit_id).cloned() } - pub fn remove_window(&mut self, id: WindowId) -> Option { - let winit_id = self.window_id_to_winit.remove(&id)?; + pub fn remove_window(&mut self, entity: Entity) -> Option { + let winit_id = self.window_id_to_winit.remove(&entity)?; // Don't remove from winit_to_window_id, to track that we used to know about this winit window self.windows.remove(&winit_id) } diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index 21e04d11b3bf2..2248ea22f0eff 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -32,11 +32,12 @@ fn setup( ..default() }); + // TODO: This needs to be re-done let window_id = WindowId::new(); // sends out a "CreateWindow" event, which will be received by the windowing backend create_window_events.send(CreateWindow { - id: window_id, + entity: window_id, descriptor: WindowDescriptor { width: 800., height: 600., From 07da9f586086a29d6dedbf64b995a215f91b39f2 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:05:11 +0200 Subject: [PATCH 02/32] Adding `WindowCommandsExtensions` for `Commands` --- crates/bevy_ecs/src/system/commands/mod.rs | 6 +++ crates/bevy_window/src/window_commands.rs | 50 ++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 crates/bevy_window/src/window_commands.rs diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index bf40e13fb98b1..8f45cd26204ae 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -62,6 +62,12 @@ impl<'w, 's> Commands<'w, 's> { Self { queue, entities } } + // TODO: Can this be solved in any other way? + /// Returns true if this commands has the entity present + pub fn has_entity(&self, entity: Entity) -> bool { + return self.entities.contains(entity); + } + /// Creates a new empty [`Entity`] and returns an [`EntityCommands`] builder for it. /// /// To directly spawn an entity with a [`Bundle`] included, you can use diff --git a/crates/bevy_window/src/window_commands.rs b/crates/bevy_window/src/window_commands.rs new file mode 100644 index 0000000000000..9855a0f4e168d --- /dev/null +++ b/crates/bevy_window/src/window_commands.rs @@ -0,0 +1,50 @@ +use bevy_ecs::{ + entity::Entity, + event::Events, + prelude::World, + system::{Command, Commands}, +}; +use bevy_math::{DVec2, IVec2, Vec2}; + +use crate::{ + window, CreateWindow, CursorIcon, PresentMode, RawWindowHandleWrapper, Window, + WindowDescriptor, WindowMode, WindowResizeConstraints, +}; + +// TODO: Docs +pub trait WindowCommandsExtension<'w, 's> { + // TODO: Docs + fn window<'a>(&'a mut self, entity: Entity) -> WindowCommands<'w, 's, 'a>; + // TODO: Docs + fn spawn_window<'a>(&'a mut self, descriptor: WindowDescriptor) -> WindowCommands<'w, 's, 'a>; +} + +impl<'w, 's> WindowCommandsExtension<'w, 's> for Commands<'w, 's> { + // TODO: Docs + /// Gives you windowcommands for an entity + fn window<'a>(&'a mut self, entity: Entity) -> WindowCommands<'w, 's, 'a> { + assert!( + self.has_entity(entity), + "Attempting to create an WindowCommands for entity {:?}, which doesn't exist.", + entity + ); + + WindowCommands { + entity, + commands: self, + } + } + + // TODO: Docs + /// Spawns and entity, then gives you window-commands for that entity + fn spawn_window<'a>(&'a mut self, descriptor: WindowDescriptor) -> WindowCommands<'w, 's, 'a> { + let entity = self.spawn().id(); + + self.add(CreateWindowCommand { entity, descriptor }); + + WindowCommands { + entity, + commands: self, + } + } +} From 0c11956d88b26755163fd3cd46a489918868b387 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:07:46 +0200 Subject: [PATCH 03/32] Exploding `Window` into multiple coponents --- crates/bevy_window/src/window.rs | 602 +++++++++++-------------------- 1 file changed, 204 insertions(+), 398 deletions(-) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 19aa6cbbb8267..c4f9bd1ecc5f5 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -1,9 +1,14 @@ +use bevy_ecs::{ + entity::Entity, + prelude::{Bundle, Component}, + system::{Command, Commands}, +}; use bevy_math::{DVec2, IVec2, Vec2}; use bevy_utils::{tracing::warn, Uuid}; use raw_window_handle::RawWindowHandle; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct WindowId(Uuid); +use crate::raw_window_handle::RawWindowHandleWrapper; +use crate::CursorIcon; /// Presentation mode for a window. /// @@ -38,35 +43,33 @@ pub enum PresentMode { Fifo = 2, // NOTE: The explicit ordinal values mirror wgpu and the vulkan spec. } -impl WindowId { - pub fn new() -> Self { - WindowId(Uuid::new_v4()) - } - - pub fn primary() -> Self { - WindowId(Uuid::from_u128(0)) - } - - pub fn is_primary(&self) -> bool { - *self == WindowId::primary() - } -} - -use crate::CursorIcon; -use std::fmt; - -use crate::raw_window_handle::RawWindowHandleWrapper; - -impl fmt::Display for WindowId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.as_simple().fmt(f) - } +/// Defines the way a window is displayed +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum WindowMode { + /// Creates a window that uses the given size + Windowed, + /// Creates a borderless window that uses the full size of the screen + BorderlessFullscreen, + /// Creates a fullscreen window that will render at desktop resolution. The app will use the closest supported size + /// from the given size and scale it to fit the screen. + SizedFullscreen, + /// Creates a fullscreen window that uses the maximum supported size + Fullscreen, } -impl Default for WindowId { - fn default() -> Self { - WindowId::primary() - } +// This should only be used by the window backend, so maybe it should not be a bundle for those reasons +// The window backend is responsible for spawning the correct components that together define a whole window +#[derive(Bundle)] +pub struct WindowBundle { + window: Window, + cursor: WindowCursor, + cursor_position: WindowCursorPosition, + handle: WindowHandle, + presentation: WindowPresentation, + mode: WindowModeComponent, + position: WindowPosition, + resolution: WindowResolution, + title: WindowTitle, } /// The size limits on a window. @@ -75,7 +78,7 @@ impl Default for WindowId { /// Please note that if the window is resizable, then when the window is /// maximized it may have a size outside of these limits. The functionality /// required to disable maximizing is not yet exposed by winit. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Component)] pub struct WindowResizeConstraints { pub min_width: f32, pub min_height: f32, @@ -126,301 +129,126 @@ impl WindowResizeConstraints { max_height, } } + + // /// The window's client resize constraint in logical pixels. + // #[inline] + // pub fn resize_constraints(&self) -> WindowResizeConstraints { + // self.resize_constraints + // } } -/// An operating system window that can present content and receive user input. -/// -/// ## Window Sizes -/// -/// There are three sizes associated with a window. The physical size which is -/// the height and width in physical pixels on the monitor. The logical size -/// which is the physical size scaled by an operating system provided factor to -/// account for monitors with differing pixel densities or user preference. And -/// the requested size, measured in logical pixels, which is the value submitted -/// to the API when creating the window, or requesting that it be resized. -/// -/// The actual size, in logical pixels, of the window may not match the -/// requested size due to operating system limits on the window size, or the -/// quantization of the logical size when converting the physical size to the -/// logical size through the scaling factor. -#[derive(Debug)] -pub struct Window { - id: WindowId, - requested_width: f32, - requested_height: f32, - physical_width: u32, - physical_height: u32, - resize_constraints: WindowResizeConstraints, - position: Option, - scale_factor_override: Option, - backend_scale_factor: f64, - title: String, - present_mode: PresentMode, - resizable: bool, - decorations: bool, +/// A marker component on an entity containing a window +#[derive(Debug, Component)] +pub struct Window; + +#[derive(Component)] +pub struct WindowCursor { cursor_icon: CursorIcon, cursor_visible: bool, cursor_locked: bool, - physical_cursor_position: Option, - raw_window_handle: RawWindowHandleWrapper, - focused: bool, - mode: WindowMode, - canvas: Option, - fit_canvas_to_parent: bool, - command_queue: Vec, -} - -#[derive(Debug)] -pub enum WindowCommand { - SetWindowMode { - mode: WindowMode, - resolution: (u32, u32), - }, - SetTitle { - title: String, - }, - SetScaleFactor { - scale_factor: f64, - }, - SetResolution { - logical_resolution: (f32, f32), - scale_factor: f64, - }, - SetPresentMode { - present_mode: PresentMode, - }, - SetResizable { - resizable: bool, - }, - SetDecorations { - decorations: bool, - }, - SetCursorLockMode { - locked: bool, - }, - SetCursorIcon { - icon: CursorIcon, - }, - SetCursorVisibility { - visible: bool, - }, - SetCursorPosition { - position: Vec2, - }, - SetMaximized { - maximized: bool, - }, - SetMinimized { - minimized: bool, - }, - SetPosition { - position: IVec2, - }, - SetResizeConstraints { - resize_constraints: WindowResizeConstraints, - }, - Close, -} - -/// Defines the way a window is displayed -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum WindowMode { - /// Creates a window that uses the given size - Windowed, - /// Creates a borderless window that uses the full size of the screen - BorderlessFullscreen, - /// Creates a fullscreen window that will render at desktop resolution. The app will use the closest supported size - /// from the given size and scale it to fit the screen. - SizedFullscreen, - /// Creates a fullscreen window that uses the maximum supported size - Fullscreen, } -impl Window { - pub fn new( - id: WindowId, - window_descriptor: &WindowDescriptor, - physical_width: u32, - physical_height: u32, - scale_factor: f64, - position: Option, - raw_window_handle: RawWindowHandle, - ) -> Self { - Window { - id, - requested_width: window_descriptor.width, - requested_height: window_descriptor.height, - position, - physical_width, - physical_height, - resize_constraints: window_descriptor.resize_constraints, - scale_factor_override: window_descriptor.scale_factor_override, - backend_scale_factor: scale_factor, - title: window_descriptor.title.clone(), - present_mode: window_descriptor.present_mode, - resizable: window_descriptor.resizable, - decorations: window_descriptor.decorations, - cursor_visible: window_descriptor.cursor_visible, - cursor_locked: window_descriptor.cursor_locked, - cursor_icon: CursorIcon::Default, - physical_cursor_position: None, - raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), - focused: true, - mode: window_descriptor.mode, - canvas: window_descriptor.canvas.clone(), - fit_canvas_to_parent: window_descriptor.fit_canvas_to_parent, - command_queue: Vec::new(), - } - } - +impl WindowCursor { #[inline] - pub fn id(&self) -> WindowId { - self.id + pub fn cursor_icon(&self) -> CursorIcon { + self.cursor_icon } - /// The current logical width of the window's client area. #[inline] - pub fn width(&self) -> f32 { - (self.physical_width as f64 / self.scale_factor()) as f32 + pub fn cursor_visible(&self) -> bool { + self.cursor_visible } - /// The current logical height of the window's client area. #[inline] - pub fn height(&self) -> f32 { - (self.physical_height as f64 / self.scale_factor()) as f32 + pub fn cursor_locked(&self) -> bool { + self.cursor_locked } - /// The requested window client area width in logical pixels from window - /// creation or the last call to [`set_resolution`](Window::set_resolution). - /// - /// This may differ from the actual width depending on OS size limits and - /// the scaling factor for high DPI monitors. - #[inline] - pub fn requested_width(&self) -> f32 { - self.requested_width + pub fn set_icon_from_backend(&mut self, icon: CursorIcon) { + self.cursor_icon = icon; } - /// The requested window client area height in logical pixels from window - /// creation or the last call to [`set_resolution`](Window::set_resolution). - /// - /// This may differ from the actual width depending on OS size limits and - /// the scaling factor for high DPI monitors. - #[inline] - pub fn requested_height(&self) -> f32 { - self.requested_height + pub fn set_visible_from_backend(&mut self, visible: bool) { + self.cursor_visible = visible; } - /// The window's client area width in physical pixels. - #[inline] - pub fn physical_width(&self) -> u32 { - self.physical_width + pub fn set_locked_from_backend(&mut self, locked: bool) { + self.cursor_locked = locked; } +} - /// The window's client area height in physical pixels. - #[inline] - pub fn physical_height(&self) -> u32 { - self.physical_height - } +#[derive(Component)] +pub struct WindowCursorPosition { + // TODO: Docs + // This is None if the cursor has left the window + physical_cursor_position: Option, +} - /// The window's client resize constraint in logical pixels. +impl WindowCursorPosition { + /// The current mouse position, in physical pixels. #[inline] - pub fn resize_constraints(&self) -> WindowResizeConstraints { - self.resize_constraints + pub fn physical_cursor_position(&self) -> Option { + self.physical_cursor_position } - /// The window's client position in physical pixels. - #[inline] - pub fn position(&self) -> Option { - self.position + // TODO: Docs + pub fn update_position_from_backend(&mut self, position: Option) { + // TODO: Fix type inconsitencies + self.physical_cursor_position = position; } +} - #[inline] - pub fn set_maximized(&mut self, maximized: bool) { - self.command_queue - .push(WindowCommand::SetMaximized { maximized }); - } +// TODO: Figure out how this connects to everything +#[derive(Component)] +pub struct WindowHandle { + raw_window_handle: RawWindowHandleWrapper, +} - /// Sets the window to minimized or back. - /// - /// # Platform-specific - /// - iOS / Android / Web: Unsupported. - /// - Wayland: Un-minimize is unsupported. - #[inline] - pub fn set_minimized(&mut self, minimized: bool) { - self.command_queue - .push(WindowCommand::SetMinimized { minimized }); - } +// TODO: Find better name +#[derive(Component)] +pub struct WindowPresentation { + present_mode: PresentMode, +} - /// Modifies the position of the window in physical pixels. - /// - /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen. - /// If the user uses a desktop with multiple monitors, the top-left hand corner of the - /// desktop is the top-left hand corner of the monitor at the top-left of the desktop. This - /// automatically un-maximizes the window if it's maximized. - /// - /// # Platform-specific - /// - /// - iOS: Can only be called on the main thread. Sets the top left coordinates of the window in - /// the screen space coordinate system. - /// - Web: Sets the top-left coordinates relative to the viewport. - /// - Android / Wayland: Unsupported. +impl WindowPresentation { #[inline] - pub fn set_position(&mut self, position: IVec2) { - self.command_queue - .push(WindowCommand::SetPosition { position }); + #[doc(alias = "vsync")] + pub fn present_mode(&self) -> PresentMode { + self.present_mode } - /// Modifies the minimum and maximum window bounds for resizing in logical pixels. - #[inline] - pub fn set_resize_constraints(&mut self, resize_constraints: WindowResizeConstraints) { - self.command_queue - .push(WindowCommand::SetResizeConstraints { resize_constraints }); + pub fn update_present_mode_from_backend(&mut self, present_mode: PresentMode) { + self.present_mode = present_mode; } +} - /// Request the OS to resize the window such the the client area matches the - /// specified width and height. - #[allow(clippy::float_cmp)] - pub fn set_resolution(&mut self, width: f32, height: f32) { - if self.requested_width == width && self.requested_height == height { - return; - } +// TODO: Find better name +#[derive(Component)] +pub struct WindowModeComponent { + mode: WindowMode, +} - self.requested_width = width; - self.requested_height = height; - self.command_queue.push(WindowCommand::SetResolution { - logical_resolution: (self.requested_width, self.requested_height), - scale_factor: self.scale_factor(), - }); +impl WindowModeComponent { + #[inline] + pub fn mode(&self) -> WindowMode { + self.mode } - /// Override the os-reported scaling factor - #[allow(clippy::float_cmp)] - pub fn set_scale_factor_override(&mut self, scale_factor: Option) { - if self.scale_factor_override == scale_factor { - return; - } - - self.scale_factor_override = scale_factor; - self.command_queue.push(WindowCommand::SetScaleFactor { - scale_factor: self.scale_factor(), - }); - self.command_queue.push(WindowCommand::SetResolution { - logical_resolution: (self.requested_width, self.requested_height), - scale_factor: self.scale_factor(), - }); + pub fn update_mode_from_backend(&mut self, mode: WindowMode) { + self.mode = mode; } +} - #[allow(missing_docs)] - #[inline] - pub fn update_scale_factor_from_backend(&mut self, scale_factor: f64) { - self.backend_scale_factor = scale_factor; - } +#[derive(Component)] +pub struct WindowPosition { + position: Option, +} - #[allow(missing_docs)] +impl WindowPosition { + /// The window's client position in physical pixels. #[inline] - pub fn update_actual_size_from_backend(&mut self, physical_width: u32, physical_height: u32) { - self.physical_width = physical_width; - self.physical_height = physical_height; + pub fn position(&self) -> Option { + self.position } #[allow(missing_docs)] @@ -428,7 +256,33 @@ impl Window { pub fn update_actual_position_from_backend(&mut self, position: IVec2) { self.position = Some(position); } +} +/// ## Window Sizes +/// +/// There are three sizes associated with a window. The physical size which is +/// the height and width in physical pixels on the monitor. The logical size +/// which is the physical size scaled by an operating system provided factor to +/// account for monitors with differing pixel densities or user preference. And +/// the requested size, measured in logical pixels, which is the value submitted +/// to the API when creating the window, or requesting that it be resized. +/// +/// The actual size, in logical pixels, of the window may not match the +/// requested size due to operating system limits on the window size, or the +/// quantization of the logical size when converting the physical size to the +/// logical size through the scaling factor. +// TODO: Make sure this is used correctly +#[derive(Component)] +pub struct WindowResolution { + requested_width: f32, + requested_height: f32, + physical_width: u32, + physical_height: u32, + scale_factor_override: Option, + backend_scale_factor: f64, +} + +impl WindowResolution { /// The ratio of physical pixels to logical pixels /// /// `physical_pixels = logical_pixels * scale_factor` @@ -449,158 +303,107 @@ impl Window { self.scale_factor_override } + /// The current logical width of the window's client area. #[inline] - pub fn title(&self) -> &str { - &self.title - } - - pub fn set_title(&mut self, title: String) { - self.title = title.to_string(); - self.command_queue.push(WindowCommand::SetTitle { title }); + pub fn width(&self) -> f32 { + (self.physical_width as f64 / self.scale_factor()) as f32 } + /// The current logical height of the window's client area. #[inline] - #[doc(alias = "vsync")] - pub fn present_mode(&self) -> PresentMode { - self.present_mode + pub fn height(&self) -> f32 { + (self.physical_height as f64 / self.scale_factor()) as f32 } + /// The requested window client area width in logical pixels from window + /// creation or the last call to [`set_resolution`](Window::set_resolution). + /// + /// This may differ from the actual width depending on OS size limits and + /// the scaling factor for high DPI monitors. + // TODO: This is never set #[inline] - #[doc(alias = "set_vsync")] - pub fn set_present_mode(&mut self, present_mode: PresentMode) { - self.present_mode = present_mode; - self.command_queue - .push(WindowCommand::SetPresentMode { present_mode }); + pub fn requested_width(&self) -> f32 { + self.requested_width } + /// The requested window client area height in logical pixels from window + /// creation or the last call to [`set_resolution`](Window::set_resolution). + /// + /// This may differ from the actual width depending on OS size limits and + /// the scaling factor for high DPI monitors. + // TODO: This is never set #[inline] - pub fn resizable(&self) -> bool { - self.resizable - } - - pub fn set_resizable(&mut self, resizable: bool) { - self.resizable = resizable; - self.command_queue - .push(WindowCommand::SetResizable { resizable }); + pub fn requested_height(&self) -> f32 { + self.requested_height } + /// The window's client area width in physical pixels. #[inline] - pub fn decorations(&self) -> bool { - self.decorations - } - - pub fn set_decorations(&mut self, decorations: bool) { - self.decorations = decorations; - self.command_queue - .push(WindowCommand::SetDecorations { decorations }); + pub fn physical_width(&self) -> u32 { + self.physical_width } + /// The window's client area height in physical pixels. #[inline] - pub fn cursor_locked(&self) -> bool { - self.cursor_locked - } - - pub fn set_cursor_lock_mode(&mut self, lock_mode: bool) { - self.cursor_locked = lock_mode; - self.command_queue - .push(WindowCommand::SetCursorLockMode { locked: lock_mode }); + pub fn physical_height(&self) -> u32 { + self.physical_height } + #[allow(missing_docs)] #[inline] - pub fn cursor_visible(&self) -> bool { - self.cursor_visible - } - - pub fn set_cursor_visibility(&mut self, visibile_mode: bool) { - self.cursor_visible = visibile_mode; - self.command_queue.push(WindowCommand::SetCursorVisibility { - visible: visibile_mode, - }); + pub fn update_scale_factor_from_backend(&mut self, scale_factor: f64) { + self.backend_scale_factor = scale_factor; } + #[allow(missing_docs)] #[inline] - pub fn cursor_icon(&self) -> CursorIcon { - self.cursor_icon - } - - pub fn set_cursor_icon(&mut self, icon: CursorIcon) { - self.command_queue - .push(WindowCommand::SetCursorIcon { icon }); + pub fn update_actual_size_from_backend(&mut self, physical_width: u32, physical_height: u32) { + self.physical_width = physical_width; + self.physical_height = physical_height; } +} - /// The current mouse position, in physical pixels. - #[inline] - pub fn physical_cursor_position(&self) -> Option { - self.physical_cursor_position - } +#[derive(Component)] +pub struct WindowTitle { + title: String, +} - /// The current mouse position, in logical pixels, taking into account the screen scale factor. +impl WindowTitle { #[inline] - #[doc(alias = "mouse position")] - pub fn cursor_position(&self) -> Option { - self.physical_cursor_position - .map(|p| (p / self.scale_factor()).as_vec2()) + pub fn title(&self) -> &str { + &self.title } - pub fn set_cursor_position(&mut self, position: Vec2) { - self.command_queue - .push(WindowCommand::SetCursorPosition { position }); + pub fn update_title_from_backend(&mut self, title: String) { + self.title = title; } +} - #[allow(missing_docs)] - #[inline] - pub fn update_focused_status_from_backend(&mut self, focused: bool) { - self.focused = focused; - } +#[derive(Component)] +pub struct WindowDecorated; - #[allow(missing_docs)] - #[inline] - pub fn update_cursor_physical_position_from_backend(&mut self, cursor_position: Option) { - self.physical_cursor_position = cursor_position; - } +#[derive(Component)] +pub struct WindowCurrentlyFocused; - #[inline] - pub fn mode(&self) -> WindowMode { - self.mode - } +#[derive(Component)] +pub struct WindowResizable; - pub fn set_mode(&mut self, mode: WindowMode) { - self.mode = mode; - self.command_queue.push(WindowCommand::SetWindowMode { - mode, - resolution: (self.physical_width, self.physical_height), - }); - } +#[derive(Component)] +pub struct WindowTransparent; - /// Close the operating system window corresponding to this [`Window`]. - /// This will also lead to this [`Window`] being removed from the - /// [`Windows`] resource. - /// - /// If the default [`WindowPlugin`] is used, when no windows are - /// open, the [app will exit](bevy_app::AppExit). - /// To disable this behaviour, set `exit_on_all_closed` on the [`WindowPlugin`] - /// to `false` - /// - /// [`Windows`]: crate::Windows - /// [`WindowPlugin`]: crate::WindowPlugin - pub fn close(&mut self) { - self.command_queue.push(WindowCommand::Close); - } +#[derive(Component)] +pub struct WindowMinimized; - #[inline] - pub fn drain_commands(&mut self) -> impl Iterator + '_ { - self.command_queue.drain(..) - } +#[derive(Component)] +pub struct WindowMaximized; - #[inline] - pub fn is_focused(&self) -> bool { - self.focused - } - - pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { - self.raw_window_handle.clone() - } +#[derive(Component)] +pub struct WindowsCanvas { + canvas: Option, + fit_canvas_to_parent: bool, +} +impl WindowsCanvas { /// The "html canvas" element selector. If set, this selector will be used to find a matching html canvas element, /// rather than creating a new one. /// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector). @@ -624,6 +427,9 @@ impl Window { } } +// /// Request the OS to resize the window such the the client area matches the +// /// specified width and height. + /// Describes the information needed for creating a window. /// /// This should be set up before adding the [`WindowPlugin`](crate::WindowPlugin). From ee35b2ae03da897a62a3944f581ba3e389d8c33a Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:08:10 +0200 Subject: [PATCH 04/32] Adding `WindowCommands` to replace methods previously existing on `Window` --- crates/bevy_window/src/window_commands.rs | 573 ++++++++++++++++++++++ 1 file changed, 573 insertions(+) diff --git a/crates/bevy_window/src/window_commands.rs b/crates/bevy_window/src/window_commands.rs index 9855a0f4e168d..c0e9657bf7d71 100644 --- a/crates/bevy_window/src/window_commands.rs +++ b/crates/bevy_window/src/window_commands.rs @@ -48,3 +48,576 @@ impl<'w, 's> WindowCommandsExtension<'w, 's> for Commands<'w, 's> { } } } + +// TODO: Docs +#[derive(Debug)] +pub struct CreateWindowCommand { + pub entity: Entity, + pub descriptor: WindowDescriptor, +} + +impl Command for CreateWindowCommand { + fn write(self, world: &mut World) { + // Make sure we only create new windows on entities that has none + if let None = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Can't create a window on an entity that already has a Window"); + } + + // match world.resource_mut::>() { + // mut create_window_event => { + // create_window_event.send(self); + // } + // _ => { + // panic!( + // "Could not send CreateWindow event as the Event has not been created" + // ); + // } + // } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetWindowModeCommand { + pub entity: Entity, + pub mode: WindowMode, + pub resolution: (u32, u32), +} + +impl Command for SetWindowModeCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetTitleCommand { + pub entity: Entity, + pub title: String, +} + +impl Command for SetTitleCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetScaleFactorCommand { + pub entity: Entity, + pub scale_factor: f64, +} + +impl Command for SetScaleFactorCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetResolutionCommand { + pub entity: Entity, + pub logical_resolution: (f32, f32), + pub scale_factor: f64, +} + +impl Command for SetResolutionCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetPresentModeCommand { + pub entity: Entity, + pub present_mode: PresentMode, +} + +impl Command for SetPresentModeCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetResizableCommand { + pub entity: Entity, + pub resizable: bool, +} + +impl Command for SetResizableCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetDecorationsCommand { + pub entity: Entity, + pub decorations: bool, +} + +impl Command for SetDecorationsCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetCursorLockModeCommand { + pub entity: Entity, + pub locked: bool, +} + +impl Command for SetCursorLockModeCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetCursorIconCommand { + pub entity: Entity, + pub icon: CursorIcon, +} + +impl Command for SetCursorIconCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetCursorVisibilityCommand { + pub entity: Entity, + pub visible: bool, +} + +impl Command for SetCursorVisibilityCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetCursorPositionCommand { + pub entity: Entity, + pub position: DVec2, +} + +impl Command for SetCursorPositionCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetMaximizedCommand { + pub entity: Entity, + pub maximized: bool, +} + +impl Command for SetMaximizedCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetMinimizedCommand { + pub entity: Entity, + pub minimized: bool, +} + +impl Command for SetMinimizedCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetPositionCommand { + pub entity: Entity, + pub position: IVec2, +} + +impl Command for SetPositionCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct SetResizeConstraintsCommand { + pub entity: Entity, + pub resize_constraints: WindowResizeConstraints, +} + +impl Command for SetResizeConstraintsCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +#[derive(Debug)] +pub struct CloseWindowCommand { + pub entity: Entity, +} + +impl Command for CloseWindowCommand { + fn write(self, world: &mut World) { + if let Some(_) = world.get::(self.entity) { + let mut event = world.resource_mut::>(); + event.send(self); + } else { + panic!("Trying to enact window commands on an entity without a window-component"); + } + } +} + +// TODO: Docs +pub struct WindowCommands<'w, 's, 'a> { + entity: Entity, + commands: &'a mut Commands<'w, 's>, +} + +impl<'w, 's, 'a> WindowCommands<'w, 's, 'a> { + // TODO: Update documentation + /// Returns the [`Entity`] id of the entity. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// fn my_system(mut commands: Commands) { + /// let entity_id = commands.spawn().id(); + /// } + /// # bevy_ecs::system::assert_is_system(my_system); + /// ``` + #[inline] + #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] + pub fn id(&self) -> Entity { + self.entity + } + + pub fn create_window(&mut self, window_desciptor: WindowDescriptor) -> &mut Self { + self.commands.add(CreateWindowCommand { + entity: self.entity, + descriptor: window_desciptor, + }); + self + } + + #[inline] + pub fn set_maximized(&mut self, maximized: bool) -> &mut Self { + self.commands.add(SetMaximizedCommand { + entity: self.entity, + maximized, + }); + self + } + + /// Sets the window to minimized or back. + /// + /// # Platform-specific + /// - iOS / Android / Web: Unsupported. + /// - Wayland: Un-minimize is unsupported. + #[inline] + pub fn set_minimized(&mut self, minimized: bool) -> &mut Self { + self.commands.add(SetMinimizedCommand { + entity: self.entity, + minimized, + }); + self + } + + /// Modifies the position of the window in physical pixels. + /// + /// Note that the top-left hand corner of the desktop is not necessarily the same as the screen. + /// If the user uses a desktop with multiple monitors, the top-left hand corner of the + /// desktop is the top-left hand corner of the monitor at the top-left of the desktop. This + /// automatically un-maximizes the window if it's maximized. + /// + /// # Platform-specific + /// + /// - iOS: Can only be called on the main thread. Sets the top left coordinates of the window in + /// the screen space coordinate system. + /// - Web: Sets the top-left coordinates relative to the viewport. + /// - Android / Wayland: Unsupported. + #[inline] + pub fn set_position(&mut self, position: IVec2) -> &mut Self { + self.commands.add(SetPositionCommand { + entity: self.entity, + position, + }); + self + } + + /// Modifies the minimum and maximum window bounds for resizing in logical pixels. + #[inline] + pub fn set_resize_constraints( + &mut self, + resize_constraints: WindowResizeConstraints, + ) -> &mut Self { + self.commands.add(SetResizeConstraintsCommand { + entity: self.entity, + resize_constraints, + }); + self + } + + /// Close the operating system window corresponding to this [`Window`]. + /// This will also lead to this [`Window`] being removed from the + /// [`Windows`] resource. + /// + /// If the default [`WindowPlugin`] is used, when no windows are + /// open, the [app will exit](bevy_app::AppExit). + /// To disable this behaviour, set `exit_on_all_closed` on the [`WindowPlugin`] + /// to `false` + /// + /// [`Windows`]: crate::Windows + /// [`WindowPlugin`]: crate::WindowPlugin + pub fn close(&mut self) -> &mut Self { + self.commands.add(CloseWindowCommand { + entity: self.entity, + }); + self + } + + pub fn set_title(&mut self, title: String) -> &mut Self { + self.commands.add(SetTitleCommand { + entity: self.entity, + title, + }); + self + } + + #[allow(clippy::float_cmp)] + pub fn set_resolution(&mut self, width: f32, height: f32, scale_factor: f64) -> &mut Self { + // TODO: Should not send the command if new is the same as old? + // if self.requested_width == width && self.requested_height == height { + // return; + // // } + self.commands.add(SetResolutionCommand { + entity: self.entity, + logical_resolution: (width, height), + scale_factor, + }); + + self + } + + /// Override the os-reported scaling factor + #[allow(clippy::float_cmp)] + pub fn set_scale_factor_override(&mut self, scale_factor: f64) -> &mut Self { + // TODO: Not do anything if new is same as old? + // if self.scale_factor_override == scale_factor { + // return; + // } + + // self.scale_factor_override = scale_factor; + self.commands.add(SetScaleFactorCommand { + entity: self.entity, + scale_factor, + }); + + self + + // TODO: Sending scale-factor event should also update the resolution + // self.commands.add(WindowCommand::SetResolution { + // logical_resolution: (self.requested_width, self.requested_height), + // scale_factor: self.scale_factor(), + // }); + } + + #[inline] + #[doc(alias = "set_vsync")] + pub fn set_present_mode(&mut self, present_mode: PresentMode) -> &mut Self { + self.commands.add(SetPresentModeCommand { + entity: self.entity, + present_mode, + }); + + self + } + + pub fn set_resizable(&mut self, resizable: bool) -> &mut Self { + self.commands.add(SetResizableCommand { + entity: self.entity, + resizable, + }); + self + } + + pub fn set_decorations(&mut self, decorations: bool) -> &mut Self { + self.commands.add(SetDecorationsCommand { + entity: self.entity, + decorations, + }); + self + } + + pub fn set_cursor_lock_mode(&mut self, lock_mode: bool) -> &mut Self { + self.commands.add(SetCursorLockModeCommand { + entity: self.entity, + locked: lock_mode, + }); + self + } + + pub fn set_cursor_visibility(&mut self, visibile_mode: bool) -> &mut Self { + self.commands.add(SetCursorVisibilityCommand { + entity: self.entity, + visible: visibile_mode, + }); + self + } + + pub fn set_cursor_icon(&mut self, icon: CursorIcon) -> &mut Self { + self.commands.add(SetCursorIconCommand { + entity: self.entity, + icon, + }); + self + } + + // TODO: This should be a resource that calculates this? + /// The current mouse position, in logical pixels, taking into account the screen scale factor. + // #[inline] + // #[doc(alias = "mouse position")] + // pub fn cursor_position(&self) -> Option { + // self.physical_cursor_position + // .map(|p| (p / self.scale_factor()).as_vec2()) + // } + + pub fn set_cursor_position(&mut self, position: DVec2) -> &mut Self { + self.commands.add(SetCursorPositionCommand { + entity: self.entity, + position, + }); + self + } + + // TODO: Backend should add or remove WindowFocused marker component + // #[allow(missing_docs)] + // #[inline] + // pub fn update_focused_status_from_backend(&mut self, focused: bool) { + // self.focused = focused; + // } + + // TODO: What to do with this? + // #[allow(missing_docs)] + // #[inline] + // pub fn update_cursor_physical_position_from_backend(&mut self, cursor_position: Option) { + // self.physical_cursor_position = cursor_position; + // } + + pub fn set_mode(&mut self, mode: WindowMode, resolution: (u32, u32)) -> &mut Self { + self.commands.add(SetWindowModeCommand { + entity: self.entity, + mode, + resolution, + }); + + self + } + + // TODO: What to do with this? + // pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { + // self.raw_window_handle.clone() + // } +} From f9a45291a784e6223120d062972b5d091c250c33 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:09:33 +0200 Subject: [PATCH 05/32] Adding new `ExitCondition` --- crates/bevy_window/src/lib.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 6e5b2abd87680..4aa93e7f95761 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -3,25 +3,25 @@ mod event; mod raw_window_handle; mod system; mod window; -mod windows; +mod window_commands; pub use crate::raw_window_handle::*; pub use cursor::*; pub use event::*; pub use system::*; pub use window::*; -pub use windows::*; +pub use window_commands::*; pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, - Window, WindowDescriptor, WindowMoved, Windows, + Window, WindowCommands, WindowCommandsExtension, WindowDescriptor, WindowMoved, }; } use bevy_app::prelude::*; -use bevy_ecs::{event::Events, schedule::SystemLabel}; +use bevy_ecs::{entity::Entity, event::Events, schedule::SystemLabel}; pub struct WindowPlugin { /// Whether to create a window when added. @@ -36,7 +36,8 @@ pub struct WindowPlugin { /// surprise your users. It is recommended to leave this setting as `true`. /// /// If true, this plugin will add [`exit_on_all_closed`] to [`CoreStage::Update`]. - pub exit_on_all_closed: bool, + // TODO: Update documentation here + pub exit_condition: ExitCondition, /// Whether to close windows when they are requested to be closed (i.e. /// when the close button is pressed) /// @@ -50,7 +51,7 @@ impl Default for WindowPlugin { fn default() -> Self { WindowPlugin { add_primary_window: true, - exit_on_all_closed: true, + exit_condition: ExitCondition::OnAllClosed, close_when_requested: true, } } @@ -88,9 +89,16 @@ impl Plugin for WindowPlugin { }); } - if self.exit_on_all_closed { - app.add_system(exit_on_all_closed); + match self.exit_condition { + ExitCondition::OnPrimaryClosed => { + app.add_system(exit_on_primary_closed); + } + ExitCondition::OnAllClosed => { + app.add_system(exit_on_all_closed); + } + ExitCondition::DontExit => {} } + if self.close_when_requested { app.add_system(close_when_requested); } @@ -99,3 +107,12 @@ impl Plugin for WindowPlugin { #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] pub struct ModifiesWindows; + +pub enum ExitCondition { + /// Close application when the primary window is closed + OnPrimaryClosed, + /// Close application when all windows are closed + OnAllClosed, + /// Keep application running headless even after closing all windows + DontExit, +} From 0e0c43cbd27e00d7229ce034b7f1d5b666f662c7 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:10:07 +0200 Subject: [PATCH 06/32] Updating bevy_window to follow new pattern more --- crates/bevy_window/src/lib.rs | 66 ++++++++++++++++++++++++++------ crates/bevy_window/src/system.rs | 63 ++++++++++++++++++++++-------- 2 files changed, 100 insertions(+), 29 deletions(-) diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 4aa93e7f95761..67af213a7cc94 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -60,7 +60,8 @@ impl Default for WindowPlugin { impl Plugin for WindowPlugin { fn build(&self, app: &mut App) { app.add_event::() - .add_event::() + // TODO: This is now moved to a command and no longer needed + // .add_event::() .add_event::() .add_event::() .add_event::() @@ -74,19 +75,42 @@ impl Plugin for WindowPlugin { .add_event::() .add_event::() .add_event::() - .init_resource::(); + // Command events + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .add_event::() + .insert_resource(PrimaryWindow::default()); if self.add_primary_window { - let window_descriptor = app - .world - .get_resource::() - .map(|descriptor| (*descriptor).clone()) - .unwrap_or_default(); - let mut create_window_event = app.world.resource_mut::>(); - create_window_event.send(CreateWindow { - id: WindowId::primary(), - descriptor: window_descriptor, - }); + // TODO: Creating window should be done through commands as entities instead of old way + app.add_startup_system(create_primary_window); + + // let window_descriptor = app + // .world + // .get_resource::() + // .map(|descriptor| (*descriptor).clone()) + // .unwrap_or_default(); + // let mut create_window_event = app.world.resource_mut::>(); + + // // TODO: Replace with commands + // create_window_event.send(CreateWindow { + // entity: WindowId::primary(), + // descriptor: window_descriptor, + // }); } match self.exit_condition { @@ -105,6 +129,7 @@ impl Plugin for WindowPlugin { } } +/// System Label marking when changes are applied to windows #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] pub struct ModifiesWindows; @@ -116,3 +141,20 @@ pub enum ExitCondition { /// Keep application running headless even after closing all windows DontExit, } + +/// Resource containing the Entity that is currently considered the primary window +pub struct PrimaryWindow { + // TODO: + // Should this be Option? + // should this be allowed to change? + // If yes, what should be responsible for updating it? + pub window: Option, +} + +impl Default for PrimaryWindow { + fn default() -> Self { + Self { + window: Option::None, + } + } +} diff --git a/crates/bevy_window/src/system.rs b/crates/bevy_window/src/system.rs index 45a374fe2e09d..b0e7b879b144b 100644 --- a/crates/bevy_window/src/system.rs +++ b/crates/bevy_window/src/system.rs @@ -1,9 +1,23 @@ -use crate::{Window, WindowCloseRequested, WindowFocused, WindowId, Windows}; +use crate::{ + window, PrimaryWindow, Window, WindowCloseRequested, WindowClosed, WindowCommandsExtension, + WindowCurrentlyFocused, WindowDescriptor, +}; use bevy_app::AppExit; use bevy_ecs::prelude::*; use bevy_input::{keyboard::KeyCode, Input}; +pub fn create_primary_window(mut commands: Commands, mut primary: ResMut) { + let entity = commands.spawn().id(); + + commands + .window(entity) + .create_window(WindowDescriptor::default()); + + // TODO: Maybe this should be controlled by window backend + primary.window = Some(entity); +} + /// Exit the application when there are no open windows. /// /// This system is added by the [`WindowPlugin`] in the default configuration. @@ -11,12 +25,31 @@ use bevy_input::{keyboard::KeyCode, Input}; /// Ensure that you read the caveats documented on that field if doing so. /// /// [`WindowPlugin`]: crate::WindowPlugin -pub fn exit_on_all_closed(mut app_exit_events: EventWriter, windows: Res) { +pub fn exit_on_all_closed(mut app_exit_events: EventWriter, windows: Query<&Window>) { if windows.iter().count() == 0 { app_exit_events.send(AppExit); } } +/// Exit the application when the primary window has been closed +/// +/// This system is added by the [`WindowPlugin`] +// TODO: More docs +pub fn exit_on_primary_closed( + mut app_exit_events: EventWriter, + primary: Res, + mut window_close: EventReader, +) { + for window in window_close.iter() { + if let Some(primary_window) = primary.window { + if primary_window == window.entity { + // Primary window has been closed + app_exit_events.send(AppExit); + } + } + } +} + /// Close windows in response to [`WindowCloseRequested`] (e.g. when the close button is pressed). /// /// This system is added by the [`WindowPlugin`] in the default configuration. @@ -24,12 +57,9 @@ pub fn exit_on_all_closed(mut app_exit_events: EventWriter, windows: Re /// Ensure that you read the caveats documented on that field if doing so. /// /// [`WindowPlugin`]: crate::WindowPlugin -pub fn close_when_requested( - mut windows: ResMut, - mut closed: EventReader, -) { +pub fn close_when_requested(mut commands: Commands, mut closed: EventReader) { for event in closed.iter() { - windows.get_mut(event.id).map(Window::close); + commands.window(event.entity).close(); } } @@ -37,21 +67,20 @@ pub fn close_when_requested( /// /// This is useful for examples or prototyping. pub fn close_on_esc( - mut focused: Local>, - mut focused_events: EventReader, - mut windows: ResMut, + mut commands: Commands, + focused_windows: Query>, + // mut focused: Local>, input: Res>, ) { + // TODO: Not quite sure what this is about // TODO: Track this in e.g. a resource to ensure consistent behaviour across similar systems - for event in focused_events.iter() { - *focused = event.focused.then(|| event.id); - } + // for event in focused_events.iter() { + // *focused = event.focused.then(|| event.id); + // } - if let Some(focused) = &*focused { + for focused_window in focused_windows.iter() { if input.just_pressed(KeyCode::Escape) { - if let Some(window) = windows.get_mut(*focused) { - window.close(); - } + commands.window(focused_window).close(); } } } From 9325098cfb35458ed5768816c207c2ebc4def84d Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:10:45 +0200 Subject: [PATCH 07/32] Deleting `Windows` as this should now be replaced by a query --- crates/bevy_window/src/windows.rs | 77 ------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 crates/bevy_window/src/windows.rs diff --git a/crates/bevy_window/src/windows.rs b/crates/bevy_window/src/windows.rs deleted file mode 100644 index df20688018133..0000000000000 --- a/crates/bevy_window/src/windows.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::{Window, WindowId}; -use bevy_utils::HashMap; - -/// A collection of [`Window`]s with unique [`WindowId`]s. -#[derive(Debug, Default)] -pub struct Windows { - windows: HashMap, -} - -impl Windows { - /// Add the provided [`Window`] to the [`Windows`] resource. - pub fn add(&mut self, window: Window) { - self.windows.insert(window.id(), window); - } - - /// Get a reference to the [`Window`] of `id` - pub fn get(&self, id: WindowId) -> Option<&Window> { - self.windows.get(&id) - } - - /// Get a mutable reference to the provided [`WindowId`]. - pub fn get_mut(&mut self, id: WindowId) -> Option<&mut Window> { - self.windows.get_mut(&id) - } - - /// Get a reference to the primary [`Window`]. - pub fn get_primary(&self) -> Option<&Window> { - self.get(WindowId::primary()) - } - - /// Get a reference to the primary [`Window`]. - /// - /// # Panics - /// - /// Panics if the primary window does not exist in [`Windows`] - pub fn primary(&self) -> &Window { - self.get_primary().expect("Primary window does not exist") - } - - /// Get a mutable reference to the primary [`Window`]. - pub fn get_primary_mut(&mut self) -> Option<&mut Window> { - self.get_mut(WindowId::primary()) - } - - /// Get a mutable reference to the primary [`Window`]. - /// - /// # Panics - /// - /// Panics if the primary window does not exist in [`Windows`] - pub fn primary_mut(&mut self) -> &mut Window { - self.get_primary_mut() - .expect("Primary window does not exist") - } - - /// Returns the scale factor for the [`Window`] of `id`, or `1.0` if the window does not exist. - pub fn scale_factor(&self, id: WindowId) -> f64 { - if let Some(window) = self.get(id) { - window.scale_factor() - } else { - 1.0 - } - } - - /// An iterator over all registered [`Window`]s - pub fn iter(&self) -> impl Iterator { - self.windows.values() - } - - /// A mutable iterator over all registered [`Window`]s - pub fn iter_mut(&mut self) -> impl Iterator { - self.windows.values_mut() - } - - pub fn remove(&mut self, id: WindowId) -> Option { - self.windows.remove(&id) - } -} From b1f57bb80810335694fa0bacdbb9e467c54bc3ad Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:17:23 +0200 Subject: [PATCH 08/32] Split `change_window` system into multiple system that handle `WindowCommand`s --- crates/bevy_winit/src/lib.rs | 181 +++-------- crates/bevy_winit/src/system.rs | 520 ++++++++++++++++++++++++++++++++ 2 files changed, 558 insertions(+), 143 deletions(-) create mode 100644 crates/bevy_winit/src/system.rs diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 40534dd8098eb..dc8fc2a3244b6 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -1,9 +1,20 @@ mod converters; +mod system; #[cfg(target_arch = "wasm32")] mod web_resize; mod winit_config; mod winit_windows; +use core::panic; + +use bevy_ecs::system::Command; +use system::{ + destroy_windows, update_cursor_icon, update_cursor_lock_mode, update_cursor_position, + update_cursor_visibility, update_decorations, update_maximized, update_minimized, + update_position, update_present_mode, update_resizable, update_resize_contraints, + update_resolution, update_scale_factor, update_title, update_window_mode, window_destroyed, +}; +use winit::window; pub use winit_config::*; pub use winit_windows::*; @@ -25,9 +36,9 @@ use bevy_utils::{ }; use bevy_window::{ CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ModifiesWindows, - ReceivedCharacter, RequestRedraw, WindowBackendScaleFactorChanged, WindowCloseRequested, - WindowClosed, WindowCreated, WindowFocused, WindowMoved, WindowResized, - WindowScaleFactorChanged, Windows, + ReceivedCharacter, RequestRedraw, Window, WindowBackendScaleFactorChanged, + WindowCloseRequested, WindowCreated, WindowCurrentlyFocused, WindowCursorPosition, + WindowFocused, WindowMoved, WindowResized, WindowResolution, WindowScaleFactorChanged, PrimaryWindow, WindowPosition, }; use winit::{ @@ -44,11 +55,34 @@ impl Plugin for WinitPlugin { app.init_non_send_resource::() .init_resource::() .set_runner(winit_runner) - .add_system_to_stage(CoreStage::PostUpdate, change_window.label(ModifiesWindows)); + // TODO: Verify that this actually works and does not cause any race-conditions or strange ordering issues + .add_system_set_to_stage( + CoreStage::PostUpdate, + SystemSet::new() + .label(ModifiesWindows) + .with_system(update_title) + .with_system(update_window_mode) + .with_system(update_decorations) + .with_system(update_scale_factor) + .with_system(update_resizable) + .with_system(update_position) + .with_system(update_minimized) + .with_system(update_maximized) + .with_system(update_resolution) + .with_system(update_cursor_icon) + .with_system(update_cursor_lock_mode) + .with_system(update_cursor_visibility) + .with_system(update_cursor_position) + .with_system(update_resize_contraints) + .with_system(update_present_mode) + .with_system(destroy_windows), // TODO: This should probably go last? + // .with_system(window_destroyed) // TODO: Unsure if this is the correct approach + ); #[cfg(target_arch = "wasm32")] app.add_plugin(web_resize::CanvasParentResizePlugin); let event_loop = EventLoop::new(); let mut create_window_reader = WinitCreateWindowReader::default(); + // TODO: Test if any issues has been caused here // Note that we create a window here "early" because WASM/WebGL requires the window to exist prior to initializing // the renderer. handle_create_window_events(&mut app.world, &event_loop, &mut create_window_reader.0); @@ -57,145 +91,6 @@ impl Plugin for WinitPlugin { } } -fn change_window( - mut winit_windows: NonSendMut, - mut windows: ResMut, - mut window_dpi_changed_events: EventWriter, - mut window_close_events: EventWriter, -) { - let mut removed_windows = vec![]; - for bevy_window in windows.iter_mut() { - let id = bevy_window.id(); - for command in bevy_window.drain_commands() { - match command { - bevy_window::WindowCommand::SetWindowMode { - mode, - resolution: (width, height), - } => { - let window = winit_windows.get_window(id).unwrap(); - match mode { - bevy_window::WindowMode::BorderlessFullscreen => { - window - .set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); - } - bevy_window::WindowMode::Fullscreen => { - window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( - get_best_videomode(&window.current_monitor().unwrap()), - ))); - } - bevy_window::WindowMode::SizedFullscreen => window.set_fullscreen(Some( - winit::window::Fullscreen::Exclusive(get_fitting_videomode( - &window.current_monitor().unwrap(), - width, - height, - )), - )), - bevy_window::WindowMode::Windowed => window.set_fullscreen(None), - } - } - bevy_window::WindowCommand::SetTitle { title } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_title(&title); - } - bevy_window::WindowCommand::SetScaleFactor { scale_factor } => { - window_dpi_changed_events.send(WindowScaleFactorChanged { id, scale_factor }); - } - bevy_window::WindowCommand::SetResolution { - logical_resolution: (width, height), - scale_factor, - } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_inner_size( - winit::dpi::LogicalSize::new(width, height) - .to_physical::(scale_factor), - ); - } - bevy_window::WindowCommand::SetPresentMode { .. } => (), - bevy_window::WindowCommand::SetResizable { resizable } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_resizable(resizable); - } - bevy_window::WindowCommand::SetDecorations { decorations } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_decorations(decorations); - } - bevy_window::WindowCommand::SetCursorIcon { icon } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_cursor_icon(converters::convert_cursor_icon(icon)); - } - bevy_window::WindowCommand::SetCursorLockMode { locked } => { - let window = winit_windows.get_window(id).unwrap(); - window - .set_cursor_grab(locked) - .unwrap_or_else(|e| error!("Unable to un/grab cursor: {}", e)); - } - bevy_window::WindowCommand::SetCursorVisibility { visible } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_cursor_visible(visible); - } - bevy_window::WindowCommand::SetCursorPosition { position } => { - let window = winit_windows.get_window(id).unwrap(); - let inner_size = window.inner_size().to_logical::(window.scale_factor()); - window - .set_cursor_position(winit::dpi::LogicalPosition::new( - position.x, - inner_size.height - position.y, - )) - .unwrap_or_else(|e| error!("Unable to set cursor position: {}", e)); - } - bevy_window::WindowCommand::SetMaximized { maximized } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_maximized(maximized); - } - bevy_window::WindowCommand::SetMinimized { minimized } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_minimized(minimized); - } - bevy_window::WindowCommand::SetPosition { position } => { - let window = winit_windows.get_window(id).unwrap(); - window.set_outer_position(PhysicalPosition { - x: position[0], - y: position[1], - }); - } - bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => { - let window = winit_windows.get_window(id).unwrap(); - let constraints = resize_constraints.check_constraints(); - let min_inner_size = LogicalSize { - width: constraints.min_width, - height: constraints.min_height, - }; - let max_inner_size = LogicalSize { - width: constraints.max_width, - height: constraints.max_height, - }; - - window.set_min_inner_size(Some(min_inner_size)); - if constraints.max_width.is_finite() && constraints.max_height.is_finite() { - window.set_max_inner_size(Some(max_inner_size)); - } - } - bevy_window::WindowCommand::Close => { - // Since we have borrowed `windows` to iterate through them, we can't remove the window from it. - // Add the removal requests to a queue to solve this - removed_windows.push(id); - // No need to run any further commands - this drops the rest of the commands, although the `bevy_window::Window` will be dropped later anyway - break; - } - } - } - } - if !removed_windows.is_empty() { - for id in removed_windows { - // Close the OS window. (The `Drop` impl actually closes the window) - let _ = winit_windows.remove_window(id); - // Clean up our own data structures - windows.remove(id); - window_close_events.send(WindowClosed { id }); - } - } -} - fn run(event_loop: EventLoop<()>, event_handler: F) -> ! where F: 'static + FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow), diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs new file mode 100644 index 0000000000000..e86af665109ea --- /dev/null +++ b/crates/bevy_winit/src/system.rs @@ -0,0 +1,520 @@ +use bevy_ecs::{ + entity::Entity, + event::{EventReader, EventWriter}, + prelude::{Added, With}, + system::{Commands, NonSendMut, Query, RemovedComponents}, +}; +use bevy_utils::tracing::error; +use bevy_window::{ + CloseWindowCommand, CreateWindow, SetCursorIconCommand, SetCursorLockModeCommand, + SetCursorPositionCommand, SetCursorVisibilityCommand, SetDecorationsCommand, + SetMaximizedCommand, SetMinimizedCommand, SetPositionCommand, SetPresentModeCommand, + SetResizableCommand, SetResizeConstraintsCommand, SetResolutionCommand, SetScaleFactorCommand, + SetTitleCommand, SetWindowModeCommand, Window, WindowBundle, WindowClosed, WindowCreated, + WindowCursor, WindowCursorPosition, WindowDecorated, WindowMaximized, WindowMinimized, + WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, WindowResolution, + WindowScaleFactorChanged, WindowTitle, WindowTransparent, +}; +use winit::{ + dpi::{LogicalSize, PhysicalPosition}, + event_loop::EventLoop, +}; + +use crate::{converters, get_best_videomode, get_fitting_videomode, WinitWindows}; + +// TODO: Docs +/// System responsible for creating new windows whenever the Event has been sent +pub(crate) fn create_windows( + mut commands: Commands, + mut event_loop: NonSendMut>, // &EventLoopWindowTarget<()>, // TODO: Not sure how this would work + mut create_window_events: EventReader, + mut window_created_events: EventWriter, + mut winit_windows: NonSendMut, + mut windows: Query>, +) { + for event in create_window_events.iter() { + // TODO: This should be about spawning the WinitWindow that corresponds + let winit_window = + winit_windows.create_window(&event_loop, event.entity, &event.descriptor); + + let mut entity_commands = commands.entity(event.entity); + + // TODO: Spawn the window bundle + + entity_commands.insert_bundle(WindowBundle { + window: Window, + cursor: WindowCursor { + cursor_icon: todo!(), + cursor_visible: todo!(), + cursor_locked: todo!(), + }, + cursor_position: todo!(), + handle: todo!(), + presentation: todo!(), + mode: todo!(), + position: todo!(), + resolution: todo!(), + title: WindowTitle { + title: event.descriptor.title, + }, + }); + + // Optional marker components + if event.descriptor.resizable { + entity_commands.insert(WindowResizable); + } + + if event.descriptor.decorations { + entity_commands.insert(WindowDecorated); + } + + if event.descriptor.transparent { + entity_commands.insert(WindowTransparent); + } + + // TODO: Window minimized + + // TODO: Window Maximized + + // TODO: Replace with separete `window_added`-system? See below + window_created_events.send(WindowCreated { + entity: event.entity, + }); + + // TODO: Fix this + #[cfg(target_arch = "wasm32")] + { + let channel = world.resource_mut::(); + if create_window_event.descriptor.fit_canvas_to_parent { + let selector = if let Some(selector) = &create_window_event.descriptor.canvas { + selector + } else { + web_resize::WINIT_CANVAS_SELECTOR + }; + channel.listen_to_selector(create_window_event.entity, selector); + } + } + } +} + +// TODO: Docs +/// System that sends a [`WindowCreated`] event once a new [`Window`] component has been created +pub(crate) fn window_added( + q: Query>, + mut writer: EventWriter, +) { + for entity in q.iter() { + writer.send(WindowCreated { entity }); + } +} + +// TODO: Docs +/// System responsible for destroying windows from commands +pub(crate) fn destroy_windows( + mut commands: Commands, + mut close_window_writer: EventWriter, + mut winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + // Close the OS window. (The `Drop` impl actually closes the window) + let _ = winit_windows.remove_window(event.entity); + + // Despawn the entity from the world + commands.entity(event.entity).despawn(); + + // Send event that the window has been closed + // TODO: Consider using the system below instead + close_window_writer.send(WindowClosed { + entity: event.entity, + }); + } +} + +// TODO: Docs +// TODO: Not sure if this is correct / better +/// System that detect that a window has been destroyed and sends an event as a result +pub(crate) fn window_destroyed( + removed: RemovedComponents, + mut writer: EventWriter, +) { + for entity in removed.iter() { + writer.send(WindowClosed { entity }); + } +} + +// TODO: Docs +pub(crate) fn update_title( + mut titles: Query<&mut WindowTitle, With>, + winit_windows: NonSendMut, + mut reader: EventReader, +) { + for event in reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + // Set the winit title + winit_window.set_title(&event.title); + // Set the title in the component + if let Ok(mut window_title) = titles.get_mut(event.entity) { + window_title.update_title_from_backend(event.title); + } else { + panic!("No WindowTitle on the entity in question"); + } + } +} + +// TODO: Docs +pub(crate) fn update_window_mode( + mut window_modes: Query<&mut WindowModeComponent, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit Window + match event.mode { + bevy_window::WindowMode::BorderlessFullscreen => { + winit_window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None))); + } + bevy_window::WindowMode::Fullscreen => { + winit_window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( + get_best_videomode(&winit_window.current_monitor().unwrap()), + ))); + } + bevy_window::WindowMode::SizedFullscreen => { + let (width, height) = event.resolution; + winit_window.set_fullscreen(Some(winit::window::Fullscreen::Exclusive( + get_fitting_videomode(&winit_window.current_monitor().unwrap(), width, height), + ))) + } + bevy_window::WindowMode::Windowed => winit_window.set_fullscreen(None), + } + + // Update components correspondinly + // TODO: Should also update resolution? + if let Ok(mut window_mode) = window_modes.get_mut(event.entity) { + window_mode.update_mode_from_backend(event.mode); + } + } +} + +// TODO: Docs +pub(crate) fn update_resolution( + mut components: Query<&mut WindowResolution, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + let (width, height) = event.logical_resolution; + + let physical_size = + winit::dpi::LogicalSize::new(width, height).to_physical::(event.scale_factor); + + // Update Winit + winit_window.set_inner_size(physical_size); + + // Update components + if let Ok(mut window_resolution) = components.get_mut(event.entity) { + // TODO: Is this casting f64 -> u32 correct / ok? + window_resolution.update_actual_size_from_backend( + physical_size.width as u32, + physical_size.height as u32, + ); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +pub(crate) fn update_cursor_position( + mut components: Query<&mut WindowCursorPosition, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + // TODO: Fix type inconsitencies + let inner_size = winit_window + .inner_size() + .to_logical::(winit_window.scale_factor()); + + // This can take either a physical position (physical pixels ) + // or logical position (logical pixels) + winit_window + .set_cursor_position(winit::dpi::LogicalPosition::new( + event.position.x, + inner_size.height - event.position.y, + )) + .unwrap_or_else(|e| error!("Unable to set cursor position: {}", e)); + + // Update components + if let Ok(mut cursor_position) = components.get_mut(event.entity) { + cursor_position.update_position_from_backend(Some(event.position)); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +// Does this need to be a command? +// TODO: Check where this is actually being used +pub(crate) fn update_resize_contraints( + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + let constraints = event.resize_constraints.check_constraints(); + let min_inner_size = LogicalSize { + width: constraints.min_width, + height: constraints.min_height, + }; + let max_inner_size = LogicalSize { + width: constraints.max_width, + height: constraints.max_height, + }; + + winit_window.set_min_inner_size(Some(min_inner_size)); + if constraints.max_width.is_finite() && constraints.max_height.is_finite() { + winit_window.set_max_inner_size(Some(max_inner_size)); + } + + // Update components + } +} + +// TODO: Docs +pub(crate) fn update_cursor_icon( + mut components: Query<&mut WindowCursor, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + winit_window.set_cursor_icon(converters::convert_cursor_icon(event.icon)); + + // Update components + if let Ok(mut window_cursor) = components.get_mut(event.entity) { + window_cursor.set_icon_from_backend(event.icon); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +pub(crate) fn update_cursor_lock_mode( + mut components: Query<&mut WindowCursor, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + winit_window + .set_cursor_grab(event.locked) + .unwrap_or_else(|e| error!("Unable to un/grab cursor: {}", e)); + + // Update components + if let Ok(mut window_cursor) = components.get_mut(event.entity) { + window_cursor.set_locked_from_backend(event.locked); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +pub(crate) fn update_cursor_visibility( + mut components: Query<&mut WindowCursor, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + winit_window.set_cursor_visible(event.visible); + + // Update components + if let Ok(mut window_cursor) = components.get_mut(event.entity) { + window_cursor.set_visible_from_backend(event.visible); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +pub(crate) fn update_present_mode( + mut components: Query<&mut WindowPresentation, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + // Update Winit + // TODO: Is there nothing that should happen here? + + // Update components + if let Ok(mut window_presentation) = components.get_mut(event.entity) { + window_presentation.update_present_mode_from_backend(event.present_mode); + } else { + // TODO: helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +pub(crate) fn update_scale_factor( + mut components: Query<&mut WindowResolution, With>, + mut window_dpi_changed_events: EventWriter, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + window_dpi_changed_events.send(WindowScaleFactorChanged { + entity: event.entity, + scale_factor: event.scale_factor, + }); + + if let Ok(mut window_resolution) = components.get_mut(event.entity) { + // TODO: Should this be scale_factor_override instead? + window_resolution.update_scale_factor_from_backend(event.scale_factor); + } else { + // TODO: Helpful panic comment + panic!(); + } + } +} + +// TODO: Docs +// TODO: What happens if you try to apply decorations on something that already has decorations, or vice versa? +pub(crate) fn update_decorations( + mut commands: Commands, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + winit_window.set_decorations(event.decorations); + + if event.decorations { + // Add decoratiosn marker + commands.entity(event.entity).insert(WindowDecorated); + } else { + // remove decoration marker + commands.entity(event.entity).remove::(); + } + } +} + +// TODO: Docs +// TODO: What happens if you try to apply resizable on something that already is resizable, or vice versa? +pub(crate) fn update_resizable( + mut commands: Commands, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + winit_window.set_resizable(event.resizable); + + if event.resizable { + // Add marker + commands.entity(event.entity).insert(WindowResizable); + } else { + // remove marker + commands.entity(event.entity).remove::(); + } + } +} + +// TODO: Docs +pub(crate) fn update_position( + mut components: Query<&mut WindowPosition, With>, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + winit_window.set_outer_position(PhysicalPosition { + x: event.position[0], + y: event.position[1], + }); + + // TODO: When will position be Option<> ? + if let Ok(mut comp) = components.get_mut(event.entity) { + comp.update_actual_position_from_backend(event.position); + } else { + // TODO: helpful panic comment + panic!() + } + } +} + +// TODO: Docs +// TODO: What happens if you try to apply minimize on something that already is minimized, or vice versa? +pub(crate) fn update_minimized( + mut commands: Commands, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + winit_window.set_minimized(event.minimized); + + if event.minimized { + // Add marker + commands.entity(event.entity).insert(WindowMinimized); + } else { + // remove marker + commands.entity(event.entity).remove::(); + } + } +} + +// TODO: Docs +// TODO: What happens if you try to apply maximize on something that already is maximized, or vice versa? +pub(crate) fn update_maximized( + mut commands: Commands, + winit_windows: NonSendMut, + mut command_reader: EventReader, +) { + for event in command_reader.iter() { + let winit_window = winit_windows.get_window(event.entity).unwrap(); + + winit_window.set_maximized(event.maximized); + + if event.maximized { + // Add marker + commands.entity(event.entity).insert(WindowMaximized); + } else { + // remove marker + commands.entity(event.entity).remove::(); + } + } +} From 29453d71ec7a06b9a5f0cba7060170eaf3b9a524 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:19:17 +0200 Subject: [PATCH 09/32] Start reworking `winit_runner()` --- crates/bevy_winit/src/lib.rs | 260 ++++++++++++++++++++++++++--------- 1 file changed, 193 insertions(+), 67 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index dc8fc2a3244b6..958901fe87e5f 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -177,7 +177,9 @@ impl Default for WinitPersistentState { #[derive(Default)] struct WinitCreateWindowReader(ManualEventReader); +// TODO: Refactor this to work with new pattern pub fn winit_runner_with(mut app: App) { + // TODO: Understand what removing and adding this does let mut event_loop = app .world .remove_non_send_resource::>() @@ -203,8 +205,21 @@ pub fn winit_runner_with(mut app: App) { match event { event::Event::NewEvents(start) => { let winit_config = app.world.resource::(); - let windows = app.world.resource::(); - let focused = windows.iter().any(|w| w.is_focused()); + + // Collection of windows + let mut windows_query = app.world.query_filtered::>(); + let windows: Vec = windows_query.iter(&app.world).collect(); + + // True if _any_ windows are currently being focused + let mut windows_focused_query = app + .world + .query_filtered::, With)>(); + let focused = windows_focused_query + .iter(&app.world) + .collect::>() + .len() + > 0; + // Check if either the `WaitUntil` timeout was triggered by winit, or that same // amount of time has elapsed since the last app update. This manual check is needed // because we don't know if the criteria for an app update were met until the end of @@ -227,13 +242,19 @@ pub fn winit_runner_with(mut app: App) { window_id: winit_window_id, .. } => { + // TODO: Should the queries happen on the world cell from this point onwards instead of app.world? let world = app.world.cell(); let winit_windows = world.non_send_resource_mut::(); - let mut windows = world.resource_mut::(); - let window_id = - if let Some(window_id) = winit_windows.get_window_id(winit_window_id) { - window_id + + // Query windows from world + let mut windows_query = app.world.query_filtered::>(); + let mut windows: Vec = windows_query.iter(&app.world).collect(); + + let window_entity = + if let Some(entity) = winit_windows.get_window_entity(winit_window_id) { + entity } else { + // TODO: This seems like it can cause problems now warn!( "Skipped event for unknown winit Window Id {:?}", winit_window_id @@ -241,29 +262,44 @@ pub fn winit_runner_with(mut app: App) { return; }; - let window = if let Some(window) = windows.get_mut(window_id) { - window - } else { - // If we're here, this window was previously opened - info!("Skipped event for closed window: {:?}", window_id); - return; - }; + // TODO: Is there an edge-case introduced by removing this? + // let window = if let Some(window) = windows.get_mut(window_entity) { + // window + // } else { + // // If we're here, this window was previously opened + // info!("Skipped event for closed window: {:?}", window_entity); + // return; + // }; winit_state.low_power_event = true; match event { WindowEvent::Resized(size) => { - window.update_actual_size_from_backend(size.width, size.height); - let mut resize_events = world.resource_mut::>(); - resize_events.send(WindowResized { - id: window_id, - width: window.width(), - height: window.height(), - }); + // TODO: + if let Some(mut resolution_component) = + app.world.get_mut::(window_entity) + { + // Update component + resolution_component + .update_actual_size_from_backend(size.width, size.height); + + // Send event to notify change + let mut resize_events = world.resource_mut::>(); + resize_events.send(WindowResized { + entity: window_entity, + width: resolution_component.width(), + height: resolution_component.height(), + }); + } else { + // TODO: Helpful panic comment + panic!("Window does not have a valid WindowResolution component"); + } } WindowEvent::CloseRequested => { let mut window_close_requested_events = world.resource_mut::>(); - window_close_requested_events.send(WindowCloseRequested { id: window_id }); + window_close_requested_events.send(WindowCloseRequested { + entity: window_entity, + }); } WindowEvent::KeyboardInput { ref input, .. } => { let mut keyboard_input_events = @@ -272,30 +308,54 @@ pub fn winit_runner_with(mut app: App) { } WindowEvent::CursorMoved { position, .. } => { let mut cursor_moved_events = world.resource_mut::>(); - let winit_window = winit_windows.get_window(window_id).unwrap(); + let winit_window = winit_windows.get_window(window_entity).unwrap(); let inner_size = winit_window.inner_size(); + // Components + // Need WindowResolution component + let window_resolution = app + .world + .get::(window_entity) + .expect("Window should have a WindowResolution component"); + + // Need cursorposition component + let mut cursor_position = app + .world + .get_mut::(window_entity) + .expect("Window should have a WindowCursorPosition component"); + + // TODO: Why is this necessary? Improve comment as to why // move origin to bottom left let y_position = inner_size.height as f64 - position.y; let physical_position = DVec2::new(position.x, y_position); - window - .update_cursor_physical_position_from_backend(Some(physical_position)); + cursor_position.update_position_from_backend(Some(physical_position)); + + // Event cursor_moved_events.send(CursorMoved { - id: window_id, - position: (physical_position / window.scale_factor()).as_vec2(), + entity: window_entity, + position: (physical_position / window_resolution.scale_factor()) + .as_vec2(), }); } WindowEvent::CursorEntered { .. } => { let mut cursor_entered_events = world.resource_mut::>(); - cursor_entered_events.send(CursorEntered { id: window_id }); + cursor_entered_events.send(CursorEntered { + entity: window_entity, + }); } WindowEvent::CursorLeft { .. } => { + // Component + let mut cursor_position = app.world.get_mut::(window_entity).expect("Window should have a WindowCursorComponent component"); + cursor_position.update_position_from_backend(None); + + // Event let mut cursor_left_events = world.resource_mut::>(); - window.update_cursor_physical_position_from_backend(None); - cursor_left_events.send(CursorLeft { id: window_id }); + cursor_left_events.send(CursorLeft { + entity: window_entity, + }); } WindowEvent::MouseInput { state, button, .. } => { let mut mouse_button_input_events = @@ -327,14 +387,21 @@ pub fn winit_runner_with(mut app: App) { }, WindowEvent::Touch(touch) => { let mut touch_input_events = world.resource_mut::>(); + + let window_resolution = app + .world + .get::(window_entity) + .expect("Window should have a WindowResolution component"); - let mut location = touch.location.to_logical(window.scale_factor()); + let mut location = touch.location.to_logical(window_resolution.scale_factor()); // On a mobile window, the start is from the top while on PC/Linux/OSX from // bottom if cfg!(target_os = "android") || cfg!(target_os = "ios") { - let window_height = windows.primary().height(); - location.y = window_height - location.y; + // Get windows_resolution of the entity currently set as primary window + let primary_window = world.resource::().window.expect("There should be a primary window but it seems that it is not"); // TODO: Update panic comment + let primary_window_resolution = app.world.get::(primary_window).expect("Primary window should have a valid WindowResolution component"); + location.y = primary_window_resolution.height() - location.y; } touch_input_events.send(converters::convert_touch_input(touch, location)); } @@ -343,7 +410,7 @@ pub fn winit_runner_with(mut app: App) { world.resource_mut::>(); char_input_events.send(ReceivedCharacter { - id: window_id, + entity: window_entity, char: c, }); } @@ -354,81 +421,112 @@ pub fn winit_runner_with(mut app: App) { let mut backend_scale_factor_change_events = world.resource_mut::>(); backend_scale_factor_change_events.send(WindowBackendScaleFactorChanged { - id: window_id, + entity: window_entity, scale_factor, }); - let prior_factor = window.scale_factor(); - window.update_scale_factor_from_backend(scale_factor); - let new_factor = window.scale_factor(); - if let Some(forced_factor) = window.scale_factor_override() { + + // Components + let mut window_resolution = app + .world + .get_mut::(window_entity) + .expect("Window should have a WindowResolution component"); + + let prior_factor = window_resolution.scale_factor(); + window_resolution.update_scale_factor_from_backend(scale_factor); + let new_factor = window_resolution.scale_factor(); + + if let Some(forced_factor) = window_resolution.scale_factor_override() { // If there is a scale factor override, then force that to be used // Otherwise, use the OS suggested size // We have already told the OS about our resize constraints, so // the new_inner_size should take those into account *new_inner_size = winit::dpi::LogicalSize::new( - window.requested_width(), - window.requested_height(), + window_resolution.requested_width(), + window_resolution.requested_height(), ) .to_physical::(forced_factor); + // TODO: Should this not trigger a WindowsScaleFactorChanged? } else if approx::relative_ne!(new_factor, prior_factor) { + // TODO: Trigger event with new scale_factor if they are approximately the same? + // Is this correct? Should it not be reversed? let mut scale_factor_change_events = world.resource_mut::>(); scale_factor_change_events.send(WindowScaleFactorChanged { - id: window_id, + entity: window_entity, scale_factor, }); } let new_logical_width = new_inner_size.width as f64 / new_factor; let new_logical_height = new_inner_size.height as f64 / new_factor; - if approx::relative_ne!(window.width() as f64, new_logical_width) - || approx::relative_ne!(window.height() as f64, new_logical_height) + if approx::relative_ne!(window_resolution.width() as f64, new_logical_width) + || approx::relative_ne!(window_resolution.height() as f64, new_logical_height) { let mut resize_events = world.resource_mut::>(); resize_events.send(WindowResized { - id: window_id, + entity: window_entity, width: new_logical_width as f32, height: new_logical_height as f32, }); } - window.update_actual_size_from_backend( + window_resolution.update_actual_size_from_backend( new_inner_size.width, new_inner_size.height, ); } WindowEvent::Focused(focused) => { - window.update_focused_status_from_backend(focused); + + // Component + let mut entity_mut = app.world.get_entity_mut(window_entity).expect("Entity for window should exist"); + + if focused { + entity_mut.insert(WindowCurrentlyFocused); + } else { + entity_mut.remove::(); + } + + // Event let mut focused_events = world.resource_mut::>(); focused_events.send(WindowFocused { - id: window_id, + entity: window_entity, focused, }); } WindowEvent::DroppedFile(path_buf) => { let mut events = world.resource_mut::>(); events.send(FileDragAndDrop::DroppedFile { - id: window_id, + entity: window_entity, path_buf, }); } WindowEvent::HoveredFile(path_buf) => { let mut events = world.resource_mut::>(); events.send(FileDragAndDrop::HoveredFile { - id: window_id, + entity: window_entity, path_buf, }); } WindowEvent::HoveredFileCancelled => { let mut events = world.resource_mut::>(); - events.send(FileDragAndDrop::HoveredFileCancelled { id: window_id }); + events.send(FileDragAndDrop::HoveredFileCancelled { + entity: window_entity, + }); } WindowEvent::Moved(position) => { let position = ivec2(position.x, position.y); - window.update_actual_position_from_backend(position); + // Component + let mut window_position = app + .world + .get_mut::(window_entity) + .expect("Window should have a WindowPosition component"); + + window_position.update_actual_position_from_backend(position); + + // Event let mut events = world.resource_mut::>(); events.send(WindowMoved { - id: window_id, + entity: window_entity, position, }); } @@ -458,8 +556,15 @@ pub fn winit_runner_with(mut app: App) { ); let winit_config = app.world.resource::(); let update = if winit_state.active { - let windows = app.world.resource::(); - let focused = windows.iter().any(|w| w.is_focused()); + // True if _any_ windows are currently being focused + let mut windows_focused_query = app + .world + .query_filtered::, With)>(); + let focused = windows_focused_query + .iter(&app.world) + .collect::>() + .len() + > 0; match winit_config.update_mode(focused) { UpdateMode::Continuous | UpdateMode::Reactive { .. } => true, UpdateMode::ReactiveLowPower { .. } => { @@ -479,8 +584,17 @@ pub fn winit_runner_with(mut app: App) { Event::RedrawEventsCleared => { { let winit_config = app.world.resource::(); - let windows = app.world.resource::(); - let focused = windows.iter().any(|w| w.is_focused()); + + // True if _any_ windows are currently being focused + let mut windows_focused_query = app + .world + .query_filtered::, With)>(); + let focused = windows_focused_query + .iter(&app.world) + .collect::>() + .len() + > 0; + let now = Instant::now(); use UpdateMode::*; *control_flow = match winit_config.update_mode(focused) { @@ -494,6 +608,7 @@ pub fn winit_runner_with(mut app: App) { } }; } + // This block needs to run after `app.update()` in `MainEventsCleared`. Otherwise, // we won't be able to see redraw requests until the next event, defeating the // purpose of a redraw request! @@ -515,6 +630,7 @@ pub fn winit_runner_with(mut app: App) { } }; + // If true, returns control from Winit back to the main Bevy loop if return_from_run { run_return(&mut event_loop, event_handler); } else { @@ -522,37 +638,47 @@ pub fn winit_runner_with(mut app: App) { } } +// TODO: Remove this is favour of the create_window system, if possible fn handle_create_window_events( world: &mut World, event_loop: &EventLoopWindowTarget<()>, create_window_event_reader: &mut ManualEventReader, ) { - let world = world.cell(); - let mut winit_windows = world.non_send_resource_mut::(); - let mut windows = world.resource_mut::(); - let create_window_events = world.resource::>(); - let mut window_created_events = world.resource_mut::>(); + // TODO: It's probably worng to be using the world directly here instead of the world-cell + // So figure out what should be the correct approach + let world_cell = world.cell(); + let mut winit_windows = world_cell.non_send_resource_mut::(); + + // Query windows from world + // let mut windows_query = world.query_filtered::>(); + // let mut windows: Vec = windows_query.iter(world).collect(); + + let create_window_events = world_cell.resource::>(); + let mut window_created_events = world_cell.resource_mut::>(); + for create_window_event in create_window_event_reader.iter(&create_window_events) { - let window = winit_windows.create_window( + let winit_windows = winit_windows.create_window( event_loop, - create_window_event.id, + create_window_event.entity, &create_window_event.descriptor, ); - windows.add(window); + + // TODO: Spawn all components required on the window-entity + window_created_events.send(WindowCreated { - id: create_window_event.id, + entity: create_window_event.entity, }); #[cfg(target_arch = "wasm32")] { - let channel = world.resource_mut::(); + let channel = world_cell.resource_mut::(); if create_window_event.descriptor.fit_canvas_to_parent { let selector = if let Some(selector) = &create_window_event.descriptor.canvas { selector } else { web_resize::WINIT_CANVAS_SELECTOR }; - channel.listen_to_selector(create_window_event.id, selector); + channel.listen_to_selector(create_window_event.entity, selector); } } } From b64d74123c7ac1c93456fbfaff7aef16e3bfc811 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:19:26 +0200 Subject: [PATCH 10/32] Add initial sketch of example --- examples/window/create_window.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/window/create_window.rs diff --git a/examples/window/create_window.rs b/examples/window/create_window.rs new file mode 100644 index 0000000000000..82741b721e3b4 --- /dev/null +++ b/examples/window/create_window.rs @@ -0,0 +1,16 @@ +//! Shows how to create and manipulate windows in bevy using WindowCommands +//! + +use bevy::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .run(); +} + +// TODO: Make example + +fn window_setup(mut commands: Commands) { + commands.window(); +} \ No newline at end of file From 1e1599b4427440e1d8ad1f43c07348cfd4446261 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 22:55:58 +0200 Subject: [PATCH 11/32] Adding notes --- crates/bevy_winit/src/lib.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 958901fe87e5f..e09909c96e52f 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -199,6 +199,8 @@ pub fn winit_runner_with(mut app: App) { trace!("Entering winit event loop"); + + let event_handler = move |event: Event<()>, event_loop: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow| { @@ -211,6 +213,7 @@ pub fn winit_runner_with(mut app: App) { let windows: Vec = windows_query.iter(&app.world).collect(); // True if _any_ windows are currently being focused + // TODO: Borrow checker complains let mut windows_focused_query = app .world .query_filtered::, With)>(); @@ -243,10 +246,12 @@ pub fn winit_runner_with(mut app: App) { .. } => { // TODO: Should the queries happen on the world cell from this point onwards instead of app.world? + // TODO: Borrow checker complains let world = app.world.cell(); let winit_windows = world.non_send_resource_mut::(); // Query windows from world + // TODO: Borrow checker complains let mut windows_query = app.world.query_filtered::>(); let mut windows: Vec = windows_query.iter(&app.world).collect(); @@ -276,6 +281,7 @@ pub fn winit_runner_with(mut app: App) { WindowEvent::Resized(size) => { // TODO: if let Some(mut resolution_component) = + // TODO: Borrow checker complains app.world.get_mut::(window_entity) { // Update component @@ -313,11 +319,13 @@ pub fn winit_runner_with(mut app: App) { // Components // Need WindowResolution component + // TODO: Borrow checker complains let window_resolution = app .world .get::(window_entity) .expect("Window should have a WindowResolution component"); + // TODO: Borrow checker complains // Need cursorposition component let mut cursor_position = app .world @@ -348,6 +356,7 @@ pub fn winit_runner_with(mut app: App) { } WindowEvent::CursorLeft { .. } => { // Component + // TODO: Borrow checker complains let mut cursor_position = app.world.get_mut::(window_entity).expect("Window should have a WindowCursorComponent component"); cursor_position.update_position_from_backend(None); @@ -388,6 +397,7 @@ pub fn winit_runner_with(mut app: App) { WindowEvent::Touch(touch) => { let mut touch_input_events = world.resource_mut::>(); + // TODO: Borrow checker complains let window_resolution = app .world .get::(window_entity) @@ -426,6 +436,7 @@ pub fn winit_runner_with(mut app: App) { }); // Components + // TODO: Borrow checker complains let mut window_resolution = app .world .get_mut::(window_entity) @@ -478,6 +489,7 @@ pub fn winit_runner_with(mut app: App) { WindowEvent::Focused(focused) => { // Component + // TODO: Borrow checker complains let mut entity_mut = app.world.get_entity_mut(window_entity).expect("Entity for window should exist"); if focused { @@ -516,6 +528,7 @@ pub fn winit_runner_with(mut app: App) { WindowEvent::Moved(position) => { let position = ivec2(position.x, position.y); // Component + // TODO: Borrow checker complains let mut window_position = app .world .get_mut::(window_entity) @@ -557,6 +570,7 @@ pub fn winit_runner_with(mut app: App) { let winit_config = app.world.resource::(); let update = if winit_state.active { // True if _any_ windows are currently being focused + // TODO: Borrow checker complains let mut windows_focused_query = app .world .query_filtered::, With)>(); @@ -586,6 +600,7 @@ pub fn winit_runner_with(mut app: App) { let winit_config = app.world.resource::(); // True if _any_ windows are currently being focused + // TODO: Borrow checker complains let mut windows_focused_query = app .world .query_filtered::, With)>(); From 00cee99c17d8a0ff966ce5cabe79d8d4d6524176 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 22:56:56 +0200 Subject: [PATCH 12/32] Fully implement spawning of created window --- crates/bevy_window/src/raw_window_handle.rs | 2 +- crates/bevy_window/src/window.rs | 16 +++++- crates/bevy_winit/src/system.rs | 61 +++++++++++++-------- crates/bevy_winit/src/winit_windows.rs | 31 +++-------- 4 files changed, 62 insertions(+), 48 deletions(-) diff --git a/crates/bevy_window/src/raw_window_handle.rs b/crates/bevy_window/src/raw_window_handle.rs index 5bc122558d78f..42d1316c06203 100644 --- a/crates/bevy_window/src/raw_window_handle.rs +++ b/crates/bevy_window/src/raw_window_handle.rs @@ -9,7 +9,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; pub struct RawWindowHandleWrapper(RawWindowHandle); impl RawWindowHandleWrapper { - pub(crate) fn new(handle: RawWindowHandle) -> Self { + pub fn new(handle: RawWindowHandle) -> Self { Self(handle) } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index c4f9bd1ecc5f5..8b6b896c84f44 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -7,7 +7,7 @@ use bevy_math::{DVec2, IVec2, Vec2}; use bevy_utils::{tracing::warn, Uuid}; use raw_window_handle::RawWindowHandle; -use crate::raw_window_handle::RawWindowHandleWrapper; +use crate::{raw_window_handle::RawWindowHandleWrapper, WindowFocused}; use crate::CursorIcon; /// Presentation mode for a window. @@ -70,6 +70,9 @@ pub struct WindowBundle { position: WindowPosition, resolution: WindowResolution, title: WindowTitle, + canvas: WindowCanvas, + resize_constraints: WindowResizeConstraints, + focused: WindowCurrentlyFocused, } /// The size limits on a window. @@ -201,9 +204,16 @@ impl WindowCursorPosition { // TODO: Figure out how this connects to everything #[derive(Component)] pub struct WindowHandle { + // TODo: What should be creating and setting this? raw_window_handle: RawWindowHandleWrapper, } +impl WindowHandle { + pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { + self.raw_window_handle.clone() + } +} + // TODO: Find better name #[derive(Component)] pub struct WindowPresentation { @@ -398,12 +408,12 @@ pub struct WindowMinimized; pub struct WindowMaximized; #[derive(Component)] -pub struct WindowsCanvas { +pub struct WindowCanvas { canvas: Option, fit_canvas_to_parent: bool, } -impl WindowsCanvas { +impl WindowCanvas { /// The "html canvas" element selector. If set, this selector will be used to find a matching html canvas element, /// rather than creating a new one. /// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector). diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index e86af665109ea..fa5f7faa32c27 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -2,8 +2,9 @@ use bevy_ecs::{ entity::Entity, event::{EventReader, EventWriter}, prelude::{Added, With}, - system::{Commands, NonSendMut, Query, RemovedComponents}, + system::{Commands, NonSendMut, Query, RemovedComponents}, schedule::IntoRunCriteria, }; +use bevy_math::IVec2; use bevy_utils::tracing::error; use bevy_window::{ CloseWindowCommand, CreateWindow, SetCursorIconCommand, SetCursorLockModeCommand, @@ -13,8 +14,9 @@ use bevy_window::{ SetTitleCommand, SetWindowModeCommand, Window, WindowBundle, WindowClosed, WindowCreated, WindowCursor, WindowCursorPosition, WindowDecorated, WindowMaximized, WindowMinimized, WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, WindowResolution, - WindowScaleFactorChanged, WindowTitle, WindowTransparent, + WindowScaleFactorChanged, WindowTitle, WindowTransparent, CursorIcon, WindowCurrentlyFocused, PresentMode, WindowHandle, RawWindowHandleWrapper, WindowCanvas, WindowResizeConstraints, }; +use raw_window_handle::HasRawWindowHandle; use winit::{ dpi::{LogicalSize, PhysicalPosition}, event_loop::EventLoop, @@ -26,11 +28,10 @@ use crate::{converters, get_best_videomode, get_fitting_videomode, WinitWindows} /// System responsible for creating new windows whenever the Event has been sent pub(crate) fn create_windows( mut commands: Commands, - mut event_loop: NonSendMut>, // &EventLoopWindowTarget<()>, // TODO: Not sure how this would work + event_loop: NonSendMut>, // &EventLoopWindowTarget<()>, // TODO: Not sure how this would work mut create_window_events: EventReader, mut window_created_events: EventWriter, mut winit_windows: NonSendMut, - mut windows: Query>, ) { for event in create_window_events.iter() { // TODO: This should be about spawning the WinitWindow that corresponds @@ -39,24 +40,43 @@ pub(crate) fn create_windows( let mut entity_commands = commands.entity(event.entity); - // TODO: Spawn the window bundle + // Prepare data + let position = winit_window + .outer_position() + .ok() + .map(|position| IVec2::new(position.x, position.y)); + let inner_size = winit_window.inner_size(); entity_commands.insert_bundle(WindowBundle { window: Window, - cursor: WindowCursor { - cursor_icon: todo!(), - cursor_visible: todo!(), - cursor_locked: todo!(), + handle: WindowHandle { raw_window_handle: RawWindowHandleWrapper::new(winit_window.raw_window_handle()) }, + presentation: WindowPresentation { present_mode: event.descriptor.present_mode }, + mode: WindowModeComponent { mode: event.descriptor.mode }, + position: WindowPosition { position }, + resolution: WindowResolution { + requested_width: event.descriptor.width, + requested_height: event.descriptor.height, + physical_width: inner_size.width, + physical_height: inner_size.height, + scale_factor_override: event.descriptor.scale_factor_override, + backend_scale_factor: winit_window.scale_factor(), }, - cursor_position: todo!(), - handle: todo!(), - presentation: todo!(), - mode: todo!(), - position: todo!(), - resolution: todo!(), title: WindowTitle { - title: event.descriptor.title, + title: event.descriptor.title.clone(), + }, + cursor_position: WindowCursorPosition { physical_cursor_position: None }, + cursor: WindowCursor { + cursor_icon: CursorIcon::Default, + cursor_visible: event.descriptor.cursor_visible, + cursor_locked: event.descriptor.cursor_locked, }, + canvas: WindowCanvas { + canvas: event.descriptor.canvas.clone(), + fit_canvas_to_parent: event.descriptor.fit_canvas_to_parent + }, + resize_constraints: event.descriptor.resize_constraints, + // TODO: All new windows must be focused? + focused: WindowCurrentlyFocused, }); // Optional marker components @@ -72,10 +92,6 @@ pub(crate) fn create_windows( entity_commands.insert(WindowTransparent); } - // TODO: Window minimized - - // TODO: Window Maximized - // TODO: Replace with separete `window_added`-system? See below window_created_events.send(WindowCreated { entity: event.entity, @@ -155,7 +171,8 @@ pub(crate) fn update_title( winit_window.set_title(&event.title); // Set the title in the component if let Ok(mut window_title) = titles.get_mut(event.entity) { - window_title.update_title_from_backend(event.title); + // TODO: Remove the clone and somehow appease the borrow-checker instead + window_title.update_title_from_backend(event.title.clone()); } else { panic!("No WindowTitle on the entity in question"); } @@ -371,7 +388,7 @@ pub(crate) fn update_present_mode( let winit_window = winit_windows.get_window(event.entity).unwrap(); // Update Winit - // TODO: Is there nothing that should happen here? + // Present mode is only relevant for the renderer, so no need to do anything to Winit at this point // Update components if let Ok(mut window_presentation) = components.get_mut(event.entity) { diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 652b07c85fb37..093c9e4059ff5 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,8 +1,6 @@ use bevy_ecs::entity::Entity; -use bevy_math::IVec2; use bevy_utils::HashMap; use bevy_window::{WindowDescriptor, WindowMode}; -use raw_window_handle::HasRawWindowHandle; use winit::dpi::LogicalSize; #[derive(Debug, Default)] @@ -22,7 +20,7 @@ impl WinitWindows { event_loop: &winit::event_loop::EventLoopWindowTarget<()>, entity: Entity, window_descriptor: &WindowDescriptor, - ) -> winit::window::Window { + ) -> &winit::window::Window { let mut winit_window_builder = winit::window::WindowBuilder::new(); winit_window_builder = match window_descriptor.mode { @@ -153,39 +151,28 @@ impl WinitWindows { } } - let position = winit_window - .outer_position() - .ok() - .map(|position| IVec2::new(position.x, position.y)); - let inner_size = winit_window.inner_size(); - let scale_factor = winit_window.scale_factor(); - let raw_window_handle = winit_window.raw_window_handle(); + // TODO: Might be more elegant ways to get return the reference of the winit-window + let id = winit_window.id(); self.windows.insert(winit_window.id(), winit_window); + let created_window = self.windows.get(&id).expect("Winit should alway have the window it just created"); + + created_window - winit_window - // TODO: This should happen through commands - // Window::new( - // entity, - // window_descriptor, - // inner_size.width, - // inner_size.height, - // scale_factor, - // position, - // raw_window_handle, - // ) } - // TODO: This might not be as useful anymore? If this is a marker component? + // TODO: Docs pub fn get_window(&self, entity: Entity) -> Option<&winit::window::Window> { self.window_id_to_winit .get(&entity) .and_then(|winit_id| self.windows.get(winit_id)) } + // TODO: Docs pub fn get_window_entity(&self, winit_id: winit::window::WindowId) -> Option { self.winit_to_window_id.get(&winit_id).cloned() } + // TODO: Docs pub fn remove_window(&mut self, entity: Entity) -> Option { let winit_id = self.window_id_to_winit.remove(&entity)?; // Don't remove from winit_to_window_id, to track that we used to know about this winit window From 7c4b8157b260c9366b67001896f1e8449998c654 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Mon, 6 Jun 2022 22:57:21 +0200 Subject: [PATCH 13/32] Start updating `bevy_render` to new patterns --- crates/bevy_render/src/camera/camera.rs | 45 +++++++++++++++---------- crates/bevy_render/src/lib.rs | 1 + crates/bevy_render/src/view/window.rs | 30 ++++++++--------- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 83f2a75a0ec07..40a18e0be7651 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -14,13 +14,13 @@ use bevy_ecs::{ event::EventReader, query::Added, reflect::ReflectComponent, - system::{Commands, ParamSet, Query, Res}, + system::{Commands, ParamSet, Query, Res}, prelude::With, }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; -use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; +use bevy_window::{WindowCreated, WindowResized, Window, WindowResolution}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, ops::Range}; use wgpu::Extent3d; @@ -97,7 +97,7 @@ impl Default for Camera { priority: 0, viewport: None, computed: Default::default(), - target: Default::default(), + target: Default::default(), // TODO: Fix default of camera depth_calculation: Default::default(), } } @@ -221,13 +221,19 @@ impl CameraRenderGraph { #[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum RenderTarget { /// Window to which the camera's view is rendered. - Window(WindowId), + Window(Entity), /// Image to which the camera's view is rendered. Image(Handle), } impl Default for RenderTarget { + // TODO: Default window ID no longer makes sense, so this can no longer impl Default fn default() -> Self { + // TODO: + // There is no longer 1 fixed id that corresponds to a valid window + // This should probably be the Entity of the PrimaryWindow + // but that is generated in runtime + // so not quite sure how to approach this Self::Window(Default::default()) } } @@ -250,16 +256,21 @@ impl RenderTarget { pub fn get_render_target_info( &self, - windows: &Windows, + windows: Query<(Entity, &WindowResolution), With>, // TODO: Maybe this could just be a Vec? images: &Assets, ) -> Option { Some(match self { RenderTarget::Window(window_id) => { - let window = windows.get(*window_id)?; - RenderTargetInfo { - physical_size: UVec2::new(window.physical_width(), window.physical_height()), - scale_factor: window.scale_factor(), + if let Ok((entity, resolution)) = windows.get(*window_id) { + RenderTargetInfo { + physical_size: UVec2::new(resolution.physical_width(), resolution.physical_height()), + scale_factor: resolution.scale_factor(), + } + } else { + // TODO: Helpful panic comment + panic!("Render target does not point to a valid window"); } + } RenderTarget::Image(image_handle) => { let image = images.get(image_handle)?; @@ -274,7 +285,7 @@ impl RenderTarget { // Check if this render target is contained in the given changed windows or images. fn is_changed( &self, - changed_window_ids: &[WindowId], + changed_window_ids: &[Entity], changed_image_handles: &HashSet<&Handle>, ) -> bool { match self { @@ -303,32 +314,32 @@ pub fn camera_system( mut window_resized_events: EventReader, mut window_created_events: EventReader, mut image_asset_events: EventReader>, - windows: Res, + windows: Query<(Entity, &WindowResolution), With>, images: Res>, mut queries: ParamSet<( Query<(Entity, &mut Camera, &mut T)>, Query>, )>, ) { - let mut changed_window_ids = Vec::new(); + let mut changed_window_ids: Vec = Vec::new(); // handle resize events. latest events are handled first because we only want to resize each // window once for event in window_resized_events.iter().rev() { - if changed_window_ids.contains(&event.id) { + if changed_window_ids.contains(&event.entity) { continue; } - changed_window_ids.push(event.id); + changed_window_ids.push(event.entity); } // handle resize events. latest events are handled first because we only want to resize each // window once for event in window_created_events.iter().rev() { - if changed_window_ids.contains(&event.id) { + if changed_window_ids.contains(&event.entity) { continue; } - changed_window_ids.push(event.id); + changed_window_ids.push(event.entity); } let changed_image_handles: HashSet<&Handle> = image_asset_events @@ -353,7 +364,7 @@ pub fn camera_system( || added_cameras.contains(&entity) || camera_projection.is_changed() { - camera.computed.target_info = camera.target.get_render_target_info(&windows, &images); + camera.computed.target_info = camera.target.get_render_target_info(windows, &images); if let Some(size) = camera.logical_viewport_size() { camera_projection.update(size.x, size.y); camera.computed.projection_matrix = camera_projection.get_projection_matrix(); diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 888d0d6865b96..c700002279043 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -129,6 +129,7 @@ impl Plugin for RenderPlugin { if let Some(backends) = options.backends { let instance = wgpu::Instance::new(backends); let surface = { + // TODO: This must be a query let windows = app.world.resource_mut::(); let raw_handle = windows.get_primary().map(|window| unsafe { let handle = window.raw_window_handle().get_handle(); diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 305a56ddd115b..1be935d27ce91 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -7,7 +7,7 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; -use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows}; +use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, Window, WindowResolution, WindowHandle, WindowPresentation }; use std::ops::{Deref, DerefMut}; use wgpu::TextureFormat; @@ -39,7 +39,7 @@ impl Plugin for WindowRenderPlugin { } pub struct ExtractedWindow { - pub id: WindowId, + pub id: Entity, pub handle: RawWindowHandleWrapper, pub physical_width: u32, pub physical_height: u32, @@ -50,11 +50,11 @@ pub struct ExtractedWindow { #[derive(Default)] pub struct ExtractedWindows { - pub windows: HashMap, + pub windows: HashMap, } impl Deref for ExtractedWindows { - type Target = HashMap; + type Target = HashMap; fn deref(&self) -> &Self::Target { &self.windows @@ -70,24 +70,24 @@ impl DerefMut for ExtractedWindows { fn extract_windows( mut render_world: ResMut, mut closed: EventReader, - windows: Res, + windows: Query<(Entity, &WindowResolution, &WindowHandle, &WindowPresentation), With> ) { let mut extracted_windows = render_world.get_resource_mut::().unwrap(); - for window in windows.iter() { + for (entity, resolution, handle, presentation) in windows.iter() { let (new_width, new_height) = ( - window.physical_width().max(1), - window.physical_height().max(1), + resolution.physical_width().max(1), + resolution.physical_height().max(1), ); let mut extracted_window = extracted_windows - .entry(window.id()) + .entry(entity) .or_insert(ExtractedWindow { - id: window.id(), - handle: window.raw_window_handle(), + id: entity, + handle: handle.raw_window_handle(), physical_width: new_width, physical_height: new_height, - present_mode: window.present_mode(), + present_mode: presentation.present_mode(), swap_chain_texture: None, size_changed: false, }); @@ -110,15 +110,15 @@ fn extract_windows( } } for closed_window in closed.iter() { - extracted_windows.remove(&closed_window.id); + extracted_windows.remove(&closed_window.entity); } } #[derive(Default)] pub struct WindowSurfaces { - surfaces: HashMap, + surfaces: HashMap, /// List of windows that we have already called the initial `configure_surface` for - configured_windows: HashSet, + configured_windows: HashSet, } pub fn prepare_windows( From 317fef335d1591d5b9b1e5a3757fe906ce8d7bd1 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 01:55:18 +0200 Subject: [PATCH 14/32] More cleanup of winit main loop --- crates/bevy_winit/src/lib.rs | 318 ++++++++++++++++++----------------- 1 file changed, 162 insertions(+), 156 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index e09909c96e52f..a8e8faeee062d 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -7,19 +7,19 @@ mod winit_windows; use core::panic; -use bevy_ecs::system::Command; +use bevy_ecs::system::{SystemState, SystemParam}; use system::{ destroy_windows, update_cursor_icon, update_cursor_lock_mode, update_cursor_position, update_cursor_visibility, update_decorations, update_maximized, update_minimized, update_position, update_present_mode, update_resizable, update_resize_contraints, - update_resolution, update_scale_factor, update_title, update_window_mode, window_destroyed, + update_resolution, update_scale_factor, update_title, update_window_mode, window_destroyed, create_windows, }; -use winit::window; + pub use winit_config::*; pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Plugin}; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, world}; use bevy_ecs::{ event::{Events, ManualEventReader}, world::World, @@ -85,6 +85,14 @@ impl Plugin for WinitPlugin { // TODO: Test if any issues has been caused here // Note that we create a window here "early" because WASM/WebGL requires the window to exist prior to initializing // the renderer. + + let world_cell = app.world.cell(); + let mut winit_windows = world_cell.non_send_resource_mut::(); + let create_window_events = world_cell.resource::>(); + let mut window_created_events = world_cell.resource_mut::>(); + + create_windows(commands, &event_loop, create_window_events, window_created_events, winit_windows); + handle_create_window_events(&mut app.world, &event_loop, &mut create_window_reader.0); app.insert_resource(create_window_reader) .insert_non_send_resource(event_loop); @@ -138,6 +146,33 @@ pub fn winit_runner(app: App) { winit_runner_with(app); } +#[derive(SystemParam)] +struct WindowEvents<'w, 's> { + window_resized: EventWriter<'w, 's, WindowResized>, + window_close_requested: EventWriter<'w, 's, WindowCloseRequested>, + window_scale_factor_changed: EventWriter<'w, 's, WindowScaleFactorChanged>, + window_backend_scale_factor_changed: EventWriter<'w, 's, WindowBackendScaleFactorChanged>, + window_focused: EventWriter<'w, 's, WindowFocused>, + window_moved: EventWriter<'w, 's, WindowMoved>, +} + +#[derive(SystemParam)] +struct InputEvents<'w, 's> { + keyboard_input: EventWriter<'w, 's, KeyboardInput>, + character_input: EventWriter<'w, 's, ReceivedCharacter>, + mouse_button_input: EventWriter<'w, 's, MouseButtonInput>, + mouse_wheel_input: EventWriter<'w, 's, MouseWheel>, + touch_input: EventWriter<'w, 's, TouchInput>, + mouse_motion: EventWriter<'w, 's, MouseMotion>, +} + +#[derive(SystemParam)] +struct CursorEvents<'w, 's> { + cursor_moved: EventWriter<'w, 's, CursorMoved>, + cursor_entered: EventWriter<'w, 's, CursorEntered>, + cursor_left: EventWriter<'w, 's, CursorLeft>, +} + // #[cfg(any( // target_os = "linux", // target_os = "dragonfly", @@ -199,8 +234,6 @@ pub fn winit_runner_with(mut app: App) { trace!("Entering winit event loop"); - - let event_handler = move |event: Event<()>, event_loop: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow| { @@ -209,19 +242,8 @@ pub fn winit_runner_with(mut app: App) { let winit_config = app.world.resource::(); // Collection of windows - let mut windows_query = app.world.query_filtered::>(); - let windows: Vec = windows_query.iter(&app.world).collect(); - - // True if _any_ windows are currently being focused - // TODO: Borrow checker complains - let mut windows_focused_query = app - .world - .query_filtered::, With)>(); - let focused = windows_focused_query - .iter(&app.world) - .collect::>() - .len() - > 0; + let mut system_state: SystemState, With)>> = SystemState::new(&mut app.world); + let any_window_focused = !system_state.get(&app.world).is_empty(); // Check if either the `WaitUntil` timeout was triggered by winit, or that same // amount of time has elapsed since the last app update. This manual check is needed @@ -229,7 +251,7 @@ pub fn winit_runner_with(mut app: App) { // the frame. let auto_timeout_reached = matches!(start, StartCause::ResumeTimeReached { .. }); let now = Instant::now(); - let manual_timeout_reached = match winit_config.update_mode(focused) { + let manual_timeout_reached = match winit_config.update_mode(any_window_focused) { UpdateMode::Continuous => false, UpdateMode::Reactive { max_wait } | UpdateMode::ReactiveLowPower { max_wait } => { @@ -245,16 +267,27 @@ pub fn winit_runner_with(mut app: App) { window_id: winit_window_id, .. } => { - // TODO: Should the queries happen on the world cell from this point onwards instead of app.world? - // TODO: Borrow checker complains - let world = app.world.cell(); - let winit_windows = world.non_send_resource_mut::(); - - // Query windows from world - // TODO: Borrow checker complains - let mut windows_query = app.world.query_filtered::>(); - let mut windows: Vec = windows_query.iter(&app.world).collect(); - + // Fetch and prepare details from the world + let mut system_state: SystemState<( + NonSend, + Query<(Entity, &mut WindowResolution, &mut WindowCursorPosition, &mut WindowPosition), With>, + Res, + WindowEvents, + InputEvents, + CursorEvents, + EventWriter, + )> = SystemState::new(&mut app.world); + let ( + winit_windows, + mut window_query, + primary_window, + mut window_events, + mut input_events, + mut cursor_events, + mut file_drag_and_drop_events + ) = system_state.get_mut(&mut app.world); + + // Entity of this window let window_entity = if let Some(entity) = winit_windows.get_window_entity(winit_window_id) { entity @@ -266,6 +299,9 @@ pub fn winit_runner_with(mut app: App) { ); return; }; + + // Reference to the Winit-window + let winit_window = winit_windows.get_window(window_entity).unwrap(); // TODO: Is there an edge-case introduced by removing this? // let window = if let Some(window) = windows.get_mut(window_entity) { @@ -279,18 +315,14 @@ pub fn winit_runner_with(mut app: App) { match event { WindowEvent::Resized(size) => { - // TODO: - if let Some(mut resolution_component) = - // TODO: Borrow checker complains - app.world.get_mut::(window_entity) + if let Ok((_, mut resolution_component, _, _)) = window_query.get_mut(window_entity) { // Update component resolution_component .update_actual_size_from_backend(size.width, size.height); // Send event to notify change - let mut resize_events = world.resource_mut::>(); - resize_events.send(WindowResized { + window_events.window_resized.send(WindowResized { entity: window_entity, width: resolution_component.width(), height: resolution_component.height(), @@ -301,36 +333,20 @@ pub fn winit_runner_with(mut app: App) { } } WindowEvent::CloseRequested => { - let mut window_close_requested_events = - world.resource_mut::>(); - window_close_requested_events.send(WindowCloseRequested { - entity: window_entity, - }); + window_events.window_close_requested + .send(WindowCloseRequested { entity: window_entity }); } WindowEvent::KeyboardInput { ref input, .. } => { - let mut keyboard_input_events = - world.resource_mut::>(); - keyboard_input_events.send(converters::convert_keyboard_input(input)); + input_events.keyboard_input + .send(converters::convert_keyboard_input(input)); } WindowEvent::CursorMoved { position, .. } => { - let mut cursor_moved_events = world.resource_mut::>(); - let winit_window = winit_windows.get_window(window_entity).unwrap(); + + // let winit_window = winit_windows.get_window(window_entity).unwrap(); let inner_size = winit_window.inner_size(); // Components - // Need WindowResolution component - // TODO: Borrow checker complains - let window_resolution = app - .world - .get::(window_entity) - .expect("Window should have a WindowResolution component"); - - // TODO: Borrow checker complains - // Need cursorposition component - let mut cursor_position = app - .world - .get_mut::(window_entity) - .expect("Window should have a WindowCursorPosition component"); + let (_, window_resolution, mut window_cursor_position, _) = window_query.get_mut(window_entity).expect("msg"); // TODO: Why is this necessary? Improve comment as to why // move origin to bottom left @@ -338,70 +354,61 @@ pub fn winit_runner_with(mut app: App) { let physical_position = DVec2::new(position.x, y_position); - cursor_position.update_position_from_backend(Some(physical_position)); + window_cursor_position.update_position_from_backend(Some(physical_position)); // Event - cursor_moved_events.send(CursorMoved { - entity: window_entity, - position: (physical_position / window_resolution.scale_factor()) - .as_vec2(), - }); + cursor_events.cursor_moved + .send(CursorMoved { + entity: window_entity, + position: (physical_position / window_resolution.scale_factor()).as_vec2(), + }); } WindowEvent::CursorEntered { .. } => { - let mut cursor_entered_events = - world.resource_mut::>(); - cursor_entered_events.send(CursorEntered { - entity: window_entity, - }); + cursor_events + .cursor_entered + .send(CursorEntered { entity: window_entity }); } WindowEvent::CursorLeft { .. } => { // Component - // TODO: Borrow checker complains - let mut cursor_position = app.world.get_mut::(window_entity).expect("Window should have a WindowCursorComponent component"); - cursor_position.update_position_from_backend(None); + let (_, _, mut window_cursor_position, _) = window_query.get_mut(window_entity).expect("Window should have a WindowCursorComponent component"); + window_cursor_position.update_position_from_backend(None); // Event - let mut cursor_left_events = world.resource_mut::>(); - cursor_left_events.send(CursorLeft { - entity: window_entity, - }); + cursor_events + .cursor_left + .send(CursorLeft { entity: window_entity }); } WindowEvent::MouseInput { state, button, .. } => { - let mut mouse_button_input_events = - world.resource_mut::>(); - mouse_button_input_events.send(MouseButtonInput { - button: converters::convert_mouse_button(button), - state: converters::convert_element_state(state), - }); + input_events + .mouse_button_input + .send(MouseButtonInput { + button: converters::convert_mouse_button(button), + state: converters::convert_element_state(state), + }); } WindowEvent::MouseWheel { delta, .. } => match delta { event::MouseScrollDelta::LineDelta(x, y) => { - let mut mouse_wheel_input_events = - world.resource_mut::>(); - mouse_wheel_input_events.send(MouseWheel { - unit: MouseScrollUnit::Line, - x, - y, - }); + input_events + .mouse_wheel_input + .send(MouseWheel { + unit: MouseScrollUnit::Line, + x, + y, + }); } event::MouseScrollDelta::PixelDelta(p) => { - let mut mouse_wheel_input_events = - world.resource_mut::>(); - mouse_wheel_input_events.send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: p.x as f32, - y: p.y as f32, - }); + input_events + .mouse_wheel_input + .send(MouseWheel { + unit: MouseScrollUnit::Pixel, + x: p.x as f32, + y: p.y as f32, + }); } }, WindowEvent::Touch(touch) => { - let mut touch_input_events = world.resource_mut::>(); - - // TODO: Borrow checker complains - let window_resolution = app - .world - .get::(window_entity) - .expect("Window should have a WindowResolution component"); + + let (_, window_resolution, _, _) = window_query.get(window_entity).expect("Window should have a WindowResolution component"); let mut location = touch.location.to_logical(window_resolution.scale_factor()); @@ -409,38 +416,38 @@ pub fn winit_runner_with(mut app: App) { // bottom if cfg!(target_os = "android") || cfg!(target_os = "ios") { // Get windows_resolution of the entity currently set as primary window - let primary_window = world.resource::().window.expect("There should be a primary window but it seems that it is not"); // TODO: Update panic comment - let primary_window_resolution = app.world.get::(primary_window).expect("Primary window should have a valid WindowResolution component"); + let primary_window_id = primary_window.window.expect("Primary window should exist"); + let (_, primary_window_resolution, _, _) = window_query.get(primary_window_id).expect("Primary window should have a valid WindowResolution component"); location.y = primary_window_resolution.height() - location.y; } - touch_input_events.send(converters::convert_touch_input(touch, location)); + + // Event + input_events + .touch_input + .send(converters::convert_touch_input(touch, location)); } WindowEvent::ReceivedCharacter(c) => { - let mut char_input_events = - world.resource_mut::>(); - - char_input_events.send(ReceivedCharacter { - entity: window_entity, - char: c, - }); + input_events + .character_input + .send(ReceivedCharacter { + entity: window_entity, + char: c, + }); } WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size, } => { - let mut backend_scale_factor_change_events = - world.resource_mut::>(); - backend_scale_factor_change_events.send(WindowBackendScaleFactorChanged { - entity: window_entity, - scale_factor, - }); + + window_events + .window_backend_scale_factor_changed + .send(WindowBackendScaleFactorChanged { + entity: window_entity, + scale_factor, + }); // Components - // TODO: Borrow checker complains - let mut window_resolution = app - .world - .get_mut::(window_entity) - .expect("Window should have a WindowResolution component"); + let (_, mut window_resolution, _) = window_query.get_mut(window_entity).expect("Window should have a WindowResolution component"); let prior_factor = window_resolution.scale_factor(); window_resolution.update_scale_factor_from_backend(scale_factor); @@ -458,15 +465,13 @@ pub fn winit_runner_with(mut app: App) { .to_physical::(forced_factor); // TODO: Should this not trigger a WindowsScaleFactorChanged? } else if approx::relative_ne!(new_factor, prior_factor) { - // TODO: Trigger event with new scale_factor if they are approximately the same? - // Is this correct? Should it not be reversed? - let mut scale_factor_change_events = - world.resource_mut::>(); - - scale_factor_change_events.send(WindowScaleFactorChanged { - entity: window_entity, - scale_factor, - }); + // Trigger a change event if they are approx different + window_events + .window_scale_factor_changed + .send(WindowScaleFactorChanged { + entity: window_entity, + scale_factor, + }); } let new_logical_width = new_inner_size.width as f64 / new_factor; @@ -474,12 +479,13 @@ pub fn winit_runner_with(mut app: App) { if approx::relative_ne!(window_resolution.width() as f64, new_logical_width) || approx::relative_ne!(window_resolution.height() as f64, new_logical_height) { - let mut resize_events = world.resource_mut::>(); - resize_events.send(WindowResized { - entity: window_entity, - width: new_logical_width as f32, - height: new_logical_height as f32, - }); + window_events + .window_resized + .send(WindowResized { + entity: window_entity, + width: new_logical_width as f32, + height: new_logical_height as f32, + }); } window_resolution.update_actual_size_from_backend( new_inner_size.width, @@ -529,19 +535,16 @@ pub fn winit_runner_with(mut app: App) { let position = ivec2(position.x, position.y); // Component // TODO: Borrow checker complains - let mut window_position = app - .world - .get_mut::(window_entity) - .expect("Window should have a WindowPosition component"); - - window_position.update_actual_position_from_backend(position); - + let (_, _, _, mut window_position) = window_query.get_mut(window_entity).expect("Window should have a WindowPosition component"); + window_position.update_actual_position_from_backend(position); + // Event - let mut events = world.resource_mut::>(); - events.send(WindowMoved { - entity: window_entity, - position, - }); + window_events + .window_moved + .send(WindowMoved { + entity: window_entity, + position, + }); } _ => {} } @@ -550,10 +553,13 @@ pub fn winit_runner_with(mut app: App) { event: DeviceEvent::MouseMotion { delta }, .. } => { - let mut mouse_motion_events = app.world.resource_mut::>(); - mouse_motion_events.send(MouseMotion { - delta: Vec2::new(delta.0 as f32, delta.1 as f32), - }); + let mut system_state: SystemState> = SystemState::new(&mut app.world); + let mut mouse_motion = system_state.get_mut(&mut app.world); + + mouse_motion + .send(MouseMotion { + delta: Vec2::new(delta.0 as f32, delta.1 as f32), + }); } event::Event::Suspended => { winit_state.active = false; From 28944d59131b0478b0c47e4066a5c4c96f4d5d90 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 01:57:04 +0200 Subject: [PATCH 15/32] minor fix --- crates/bevy_winit/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index a8e8faeee062d..f3a1a3b3ad372 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -447,7 +447,7 @@ pub fn winit_runner_with(mut app: App) { }); // Components - let (_, mut window_resolution, _) = window_query.get_mut(window_entity).expect("Window should have a WindowResolution component"); + let (_, mut window_resolution, _, _) = window_query.get_mut(window_entity).expect("Window should have a WindowResolution component"); let prior_factor = window_resolution.scale_factor(); window_resolution.update_scale_factor_from_backend(scale_factor); From 1d95cdbf7f2b4de76580f48a10a624fc853ad2e2 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 02:01:02 +0200 Subject: [PATCH 16/32] And more tweaks --- crates/bevy_winit/src/lib.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index f3a1a3b3ad372..f80b5fb7e675e 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -498,6 +498,7 @@ pub fn winit_runner_with(mut app: App) { // TODO: Borrow checker complains let mut entity_mut = app.world.get_entity_mut(window_entity).expect("Entity for window should exist"); + // TODO: How to insert and remove components and still pleasing the borrow checker? if focused { entity_mut.insert(WindowCurrentlyFocused); } else { @@ -505,29 +506,27 @@ pub fn winit_runner_with(mut app: App) { } // Event - let mut focused_events = world.resource_mut::>(); - focused_events.send(WindowFocused { - entity: window_entity, - focused, - }); + window_events + .window_focused + .send(WindowFocused { + entity: window_entity, + focused, + }); } WindowEvent::DroppedFile(path_buf) => { - let mut events = world.resource_mut::>(); - events.send(FileDragAndDrop::DroppedFile { + file_drag_and_drop_events.send(FileDragAndDrop::DroppedFile { entity: window_entity, path_buf, }); } WindowEvent::HoveredFile(path_buf) => { - let mut events = world.resource_mut::>(); - events.send(FileDragAndDrop::HoveredFile { + file_drag_and_drop_events.send(FileDragAndDrop::HoveredFile { entity: window_entity, path_buf, }); } WindowEvent::HoveredFileCancelled => { - let mut events = world.resource_mut::>(); - events.send(FileDragAndDrop::HoveredFileCancelled { + file_drag_and_drop_events.send(FileDragAndDrop::HoveredFileCancelled { entity: window_entity, }); } From 8df34dc8dcbdb2a9fb0056c9e66ca59bbb72e2bb Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:58:57 +0200 Subject: [PATCH 17/32] continuing refactor of winit-loop --- crates/bevy_winit/src/lib.rs | 389 ++++++++++++++++++----------------- 1 file changed, 198 insertions(+), 191 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index f80b5fb7e675e..c41ef7859fe4c 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -7,23 +7,25 @@ mod winit_windows; use core::panic; -use bevy_ecs::system::{SystemState, SystemParam}; +use bevy_ecs::system::{SystemParam, SystemState}; use system::{ - destroy_windows, update_cursor_icon, update_cursor_lock_mode, update_cursor_position, - update_cursor_visibility, update_decorations, update_maximized, update_minimized, - update_position, update_present_mode, update_resizable, update_resize_contraints, - update_resolution, update_scale_factor, update_title, update_window_mode, window_destroyed, create_windows, + create_windows, destroy_windows, update_cursor_icon, update_cursor_lock_mode, + update_cursor_position, update_cursor_visibility, update_decorations, update_maximized, + update_minimized, update_position, update_present_mode, update_resizable, + update_resize_contraints, update_resolution, update_scale_factor, update_title, + update_window_mode, window_destroyed, }; +use winit::event_loop; pub use winit_config::*; pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Plugin}; -use bevy_ecs::{prelude::*, world}; use bevy_ecs::{ event::{Events, ManualEventReader}, world::World, }; +use bevy_ecs::{prelude::*, world}; use bevy_input::{ keyboard::KeyboardInput, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, @@ -36,9 +38,10 @@ use bevy_utils::{ }; use bevy_window::{ CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ModifiesWindows, - ReceivedCharacter, RequestRedraw, Window, WindowBackendScaleFactorChanged, + PrimaryWindow, ReceivedCharacter, RequestRedraw, Window, WindowBackendScaleFactorChanged, WindowCloseRequested, WindowCreated, WindowCurrentlyFocused, WindowCursorPosition, - WindowFocused, WindowMoved, WindowResized, WindowResolution, WindowScaleFactorChanged, PrimaryWindow, WindowPosition, + WindowFocused, WindowMoved, WindowPosition, WindowResized, WindowResolution, + WindowScaleFactorChanged, }; use winit::{ @@ -80,22 +83,38 @@ impl Plugin for WinitPlugin { ); #[cfg(target_arch = "wasm32")] app.add_plugin(web_resize::CanvasParentResizePlugin); + let event_loop = EventLoop::new(); let mut create_window_reader = WinitCreateWindowReader::default(); // TODO: Test if any issues has been caused here // Note that we create a window here "early" because WASM/WebGL requires the window to exist prior to initializing // the renderer. - - let world_cell = app.world.cell(); - let mut winit_windows = world_cell.non_send_resource_mut::(); - let create_window_events = world_cell.resource::>(); - let mut window_created_events = world_cell.resource_mut::>(); - - create_windows(commands, &event_loop, create_window_events, window_created_events, winit_windows); - - handle_create_window_events(&mut app.world, &event_loop, &mut create_window_reader.0); app.insert_resource(create_window_reader) .insert_non_send_resource(event_loop); + + let mut system_state: SystemState<( + Commands, + EventReader, + EventWriter, + NonSendMut, + NonSendMut>, + )> = SystemState::new(&mut app.world); + let ( + mut commands, + mut create_window_events, + mut window_created_events, + mut winit_windows, + mut event_loop, + ) = system_state.get_mut(&mut app.world); + + // Run it once here in startup. It will be run again on the MainEventsCleared update event + create_windows( + commands, + event_loop, + create_window_events, + window_created_events, + winit_windows, + ); } } @@ -237,13 +256,19 @@ pub fn winit_runner_with(mut app: App) { let event_handler = move |event: Event<()>, event_loop: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow| { + // TODO move all system state fetch up here? + match event { event::Event::NewEvents(start) => { - let winit_config = app.world.resource::(); + // Fetch from the world + let mut system_state: SystemState<( + Res, + Query, With)>, + )> = SystemState::new(&mut app.world); + + let (winit_config, window_focused_query) = system_state.get(&mut app.world); - // Collection of windows - let mut system_state: SystemState, With)>> = SystemState::new(&mut app.world); - let any_window_focused = !system_state.get(&app.world).is_empty(); + let any_window_focused = !window_focused_query.is_empty(); // Check if either the `WaitUntil` timeout was triggered by winit, or that same // amount of time has elapsed since the last app update. This manual check is needed @@ -269,8 +294,17 @@ pub fn winit_runner_with(mut app: App) { } => { // Fetch and prepare details from the world let mut system_state: SystemState<( - NonSend, - Query<(Entity, &mut WindowResolution, &mut WindowCursorPosition, &mut WindowPosition), With>, + Commands, + NonSend, + Query< + ( + Entity, + &mut WindowResolution, + &mut WindowCursorPosition, + &mut WindowPosition, + ), + With, + >, Res, WindowEvents, InputEvents, @@ -278,13 +312,14 @@ pub fn winit_runner_with(mut app: App) { EventWriter, )> = SystemState::new(&mut app.world); let ( + mut commands, winit_windows, mut window_query, primary_window, mut window_events, mut input_events, mut cursor_events, - mut file_drag_and_drop_events + mut file_drag_and_drop_events, ) = system_state.get_mut(&mut app.world); // Entity of this window @@ -299,7 +334,7 @@ pub fn winit_runner_with(mut app: App) { ); return; }; - + // Reference to the Winit-window let winit_window = winit_windows.get_window(window_entity).unwrap(); @@ -315,7 +350,8 @@ pub fn winit_runner_with(mut app: App) { match event { WindowEvent::Resized(size) => { - if let Ok((_, mut resolution_component, _, _)) = window_query.get_mut(window_entity) + if let Ok((_, mut resolution_component, _, _)) = + window_query.get_mut(window_entity) { // Update component resolution_component @@ -333,20 +369,24 @@ pub fn winit_runner_with(mut app: App) { } } WindowEvent::CloseRequested => { - window_events.window_close_requested - .send(WindowCloseRequested { entity: window_entity }); + window_events + .window_close_requested + .send(WindowCloseRequested { + entity: window_entity, + }); } WindowEvent::KeyboardInput { ref input, .. } => { - input_events.keyboard_input + input_events + .keyboard_input .send(converters::convert_keyboard_input(input)); } WindowEvent::CursorMoved { position, .. } => { - // let winit_window = winit_windows.get_window(window_entity).unwrap(); let inner_size = winit_window.inner_size(); // Components - let (_, window_resolution, mut window_cursor_position, _) = window_query.get_mut(window_entity).expect("msg"); + let (_, window_resolution, mut window_cursor_position, _) = + window_query.get_mut(window_entity).expect("msg"); // TODO: Why is this necessary? Improve comment as to why // move origin to bottom left @@ -354,70 +394,73 @@ pub fn winit_runner_with(mut app: App) { let physical_position = DVec2::new(position.x, y_position); - window_cursor_position.update_position_from_backend(Some(physical_position)); + window_cursor_position + .update_position_from_backend(Some(physical_position)); // Event - cursor_events.cursor_moved - .send(CursorMoved { - entity: window_entity, - position: (physical_position / window_resolution.scale_factor()).as_vec2(), - }); + cursor_events.cursor_moved.send(CursorMoved { + entity: window_entity, + position: (physical_position / window_resolution.scale_factor()) + .as_vec2(), + }); } WindowEvent::CursorEntered { .. } => { - cursor_events - .cursor_entered - .send(CursorEntered { entity: window_entity }); + cursor_events.cursor_entered.send(CursorEntered { + entity: window_entity, + }); } WindowEvent::CursorLeft { .. } => { // Component - let (_, _, mut window_cursor_position, _) = window_query.get_mut(window_entity).expect("Window should have a WindowCursorComponent component"); + let (_, _, mut window_cursor_position, _) = window_query + .get_mut(window_entity) + .expect("Window should have a WindowCursorComponent component"); window_cursor_position.update_position_from_backend(None); // Event - cursor_events - .cursor_left - .send(CursorLeft { entity: window_entity }); + cursor_events.cursor_left.send(CursorLeft { + entity: window_entity, + }); } WindowEvent::MouseInput { state, button, .. } => { - input_events - .mouse_button_input - .send(MouseButtonInput { - button: converters::convert_mouse_button(button), - state: converters::convert_element_state(state), - }); + input_events.mouse_button_input.send(MouseButtonInput { + button: converters::convert_mouse_button(button), + state: converters::convert_element_state(state), + }); } WindowEvent::MouseWheel { delta, .. } => match delta { event::MouseScrollDelta::LineDelta(x, y) => { - input_events - .mouse_wheel_input - .send(MouseWheel { - unit: MouseScrollUnit::Line, - x, - y, - }); + input_events.mouse_wheel_input.send(MouseWheel { + unit: MouseScrollUnit::Line, + x, + y, + }); } event::MouseScrollDelta::PixelDelta(p) => { - input_events - .mouse_wheel_input - .send(MouseWheel { - unit: MouseScrollUnit::Pixel, - x: p.x as f32, - y: p.y as f32, - }); + input_events.mouse_wheel_input.send(MouseWheel { + unit: MouseScrollUnit::Pixel, + x: p.x as f32, + y: p.y as f32, + }); } }, WindowEvent::Touch(touch) => { + let (_, window_resolution, _, _) = window_query + .get(window_entity) + .expect("Window should have a WindowResolution component"); - let (_, window_resolution, _, _) = window_query.get(window_entity).expect("Window should have a WindowResolution component"); - - let mut location = touch.location.to_logical(window_resolution.scale_factor()); + let mut location = + touch.location.to_logical(window_resolution.scale_factor()); // On a mobile window, the start is from the top while on PC/Linux/OSX from // bottom if cfg!(target_os = "android") || cfg!(target_os = "ios") { // Get windows_resolution of the entity currently set as primary window - let primary_window_id = primary_window.window.expect("Primary window should exist"); - let (_, primary_window_resolution, _, _) = window_query.get(primary_window_id).expect("Primary window should have a valid WindowResolution component"); + let primary_window_id = + primary_window.window.expect("Primary window should exist"); + let (_, primary_window_resolution, _, _) = + window_query.get(primary_window_id).expect( + "Primary window should have a valid WindowResolution component", + ); location.y = primary_window_resolution.height() - location.y; } @@ -427,27 +470,26 @@ pub fn winit_runner_with(mut app: App) { .send(converters::convert_touch_input(touch, location)); } WindowEvent::ReceivedCharacter(c) => { - input_events - .character_input - .send(ReceivedCharacter { - entity: window_entity, - char: c, - }); + input_events.character_input.send(ReceivedCharacter { + entity: window_entity, + char: c, + }); } WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size, } => { - - window_events - .window_backend_scale_factor_changed - .send(WindowBackendScaleFactorChanged { + window_events.window_backend_scale_factor_changed.send( + WindowBackendScaleFactorChanged { entity: window_entity, scale_factor, - }); + }, + ); // Components - let (_, mut window_resolution, _, _) = window_query.get_mut(window_entity).expect("Window should have a WindowResolution component"); + let (_, mut window_resolution, _, _) = window_query + .get_mut(window_entity) + .expect("Window should have a WindowResolution component"); let prior_factor = window_resolution.scale_factor(); window_resolution.update_scale_factor_from_backend(scale_factor); @@ -465,27 +507,28 @@ pub fn winit_runner_with(mut app: App) { .to_physical::(forced_factor); // TODO: Should this not trigger a WindowsScaleFactorChanged? } else if approx::relative_ne!(new_factor, prior_factor) { - // Trigger a change event if they are approx different - window_events - .window_scale_factor_changed - .send(WindowScaleFactorChanged { + // Trigger a change event if they are approximately different + window_events.window_scale_factor_changed.send( + WindowScaleFactorChanged { entity: window_entity, scale_factor, - }); + }, + ); } let new_logical_width = new_inner_size.width as f64 / new_factor; let new_logical_height = new_inner_size.height as f64 / new_factor; if approx::relative_ne!(window_resolution.width() as f64, new_logical_width) - || approx::relative_ne!(window_resolution.height() as f64, new_logical_height) + || approx::relative_ne!( + window_resolution.height() as f64, + new_logical_height + ) { - window_events - .window_resized - .send(WindowResized { - entity: window_entity, - width: new_logical_width as f32, - height: new_logical_height as f32, - }); + window_events.window_resized.send(WindowResized { + entity: window_entity, + width: new_logical_width as f32, + height: new_logical_height as f32, + }); } window_resolution.update_actual_size_from_backend( new_inner_size.width, @@ -493,25 +536,22 @@ pub fn winit_runner_with(mut app: App) { ); } WindowEvent::Focused(focused) => { - // Component - // TODO: Borrow checker complains - let mut entity_mut = app.world.get_entity_mut(window_entity).expect("Entity for window should exist"); - - // TODO: How to insert and remove components and still pleasing the borrow checker? if focused { - entity_mut.insert(WindowCurrentlyFocused); + commands + .entity(window_entity) + .insert(WindowCurrentlyFocused); } else { - entity_mut.remove::(); + commands + .entity(window_entity) + .remove::(); } // Event - window_events - .window_focused - .send(WindowFocused { - entity: window_entity, - focused, - }); + window_events.window_focused.send(WindowFocused { + entity: window_entity, + focused, + }); } WindowEvent::DroppedFile(path_buf) => { file_drag_and_drop_events.send(FileDragAndDrop::DroppedFile { @@ -532,18 +572,18 @@ pub fn winit_runner_with(mut app: App) { } WindowEvent::Moved(position) => { let position = ivec2(position.x, position.y); + // Component - // TODO: Borrow checker complains - let (_, _, _, mut window_position) = window_query.get_mut(window_entity).expect("Window should have a WindowPosition component"); + let (_, _, _, mut window_position) = window_query + .get_mut(window_entity) + .expect("Window should have a WindowPosition component"); window_position.update_actual_position_from_backend(position); - + // Event - window_events - .window_moved - .send(WindowMoved { - entity: window_entity, - position, - }); + window_events.window_moved.send(WindowMoved { + entity: window_entity, + position, + }); } _ => {} } @@ -552,13 +592,13 @@ pub fn winit_runner_with(mut app: App) { event: DeviceEvent::MouseMotion { delta }, .. } => { - let mut system_state: SystemState> = SystemState::new(&mut app.world); + let mut system_state: SystemState> = + SystemState::new(&mut app.world); let mut mouse_motion = system_state.get_mut(&mut app.world); - - mouse_motion - .send(MouseMotion { - delta: Vec2::new(delta.0 as f32, delta.1 as f32), - }); + + mouse_motion.send(MouseMotion { + delta: Vec2::new(delta.0 as f32, delta.1 as f32), + }); } event::Event::Suspended => { winit_state.active = false; @@ -567,23 +607,38 @@ pub fn winit_runner_with(mut app: App) { winit_state.active = true; } event::Event::MainEventsCleared => { - handle_create_window_events( - &mut app.world, + let mut system_state: SystemState<( + Commands, + EventReader, + EventWriter, + NonSendMut, + NonSendMut>, + Res, + Query, With)>, + )> = SystemState::new(&mut app.world); + let ( + mut commands, + mut create_window_events, + mut window_created_events, + mut winit_windows, + mut event_loop, + winit_config, + window_focused_query, + ) = system_state.get_mut(&mut app.world); + + // Responsible for creating new windows + create_windows( + commands, event_loop, - &mut create_window_event_reader, + create_window_events, + window_created_events, + winit_windows, ); - let winit_config = app.world.resource::(); + let update = if winit_state.active { // True if _any_ windows are currently being focused - // TODO: Borrow checker complains - let mut windows_focused_query = app - .world - .query_filtered::, With)>(); - let focused = windows_focused_query - .iter(&app.world) - .collect::>() - .len() - > 0; + // TODO: Do we need to fetch windows again since new ones might have been created and they might be focused? + let focused = !window_focused_query.is_empty(); match winit_config.update_mode(focused) { UpdateMode::Continuous | UpdateMode::Reactive { .. } => true, UpdateMode::ReactiveLowPower { .. } => { @@ -602,18 +657,16 @@ pub fn winit_runner_with(mut app: App) { } Event::RedrawEventsCleared => { { - let winit_config = app.world.resource::(); + // Fetch from world + let mut system_state: SystemState<( + Res, + Query, With)>, + )> = SystemState::new(&mut app.world); + + let (winit_config, window_focused_query) = system_state.get(&mut app.world); // True if _any_ windows are currently being focused - // TODO: Borrow checker complains - let mut windows_focused_query = app - .world - .query_filtered::, With)>(); - let focused = windows_focused_query - .iter(&app.world) - .collect::>() - .len() - > 0; + let focused = !window_focused_query.is_empty(); let now = Instant::now(); use UpdateMode::*; @@ -657,49 +710,3 @@ pub fn winit_runner_with(mut app: App) { run(event_loop, event_handler); } } - -// TODO: Remove this is favour of the create_window system, if possible -fn handle_create_window_events( - world: &mut World, - event_loop: &EventLoopWindowTarget<()>, - create_window_event_reader: &mut ManualEventReader, -) { - // TODO: It's probably worng to be using the world directly here instead of the world-cell - // So figure out what should be the correct approach - let world_cell = world.cell(); - let mut winit_windows = world_cell.non_send_resource_mut::(); - - // Query windows from world - // let mut windows_query = world.query_filtered::>(); - // let mut windows: Vec = windows_query.iter(world).collect(); - - let create_window_events = world_cell.resource::>(); - let mut window_created_events = world_cell.resource_mut::>(); - - for create_window_event in create_window_event_reader.iter(&create_window_events) { - let winit_windows = winit_windows.create_window( - event_loop, - create_window_event.entity, - &create_window_event.descriptor, - ); - - // TODO: Spawn all components required on the window-entity - - window_created_events.send(WindowCreated { - entity: create_window_event.entity, - }); - - #[cfg(target_arch = "wasm32")] - { - let channel = world_cell.resource_mut::(); - if create_window_event.descriptor.fit_canvas_to_parent { - let selector = if let Some(selector) = &create_window_event.descriptor.canvas { - selector - } else { - web_resize::WINIT_CANVAS_SELECTOR - }; - channel.listen_to_selector(create_window_event.entity, selector); - } - } - } -} From 537e4b043d35b21cdf185339d7c1961fc8da6649 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:59:37 +0200 Subject: [PATCH 18/32] Finish setting up components for new windows --- crates/bevy_window/src/window.rs | 90 +++++++++++++++++++++++++++----- crates/bevy_winit/src/system.rs | 79 ++++++++++++++-------------- 2 files changed, 116 insertions(+), 53 deletions(-) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8b6b896c84f44..08ed3fb8727b6 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -7,8 +7,8 @@ use bevy_math::{DVec2, IVec2, Vec2}; use bevy_utils::{tracing::warn, Uuid}; use raw_window_handle::RawWindowHandle; -use crate::{raw_window_handle::RawWindowHandleWrapper, WindowFocused}; use crate::CursorIcon; +use crate::{raw_window_handle::RawWindowHandleWrapper, WindowFocused}; /// Presentation mode for a window. /// @@ -61,18 +61,18 @@ pub enum WindowMode { // The window backend is responsible for spawning the correct components that together define a whole window #[derive(Bundle)] pub struct WindowBundle { - window: Window, - cursor: WindowCursor, - cursor_position: WindowCursorPosition, - handle: WindowHandle, - presentation: WindowPresentation, - mode: WindowModeComponent, - position: WindowPosition, - resolution: WindowResolution, - title: WindowTitle, - canvas: WindowCanvas, - resize_constraints: WindowResizeConstraints, - focused: WindowCurrentlyFocused, + pub window: Window, + pub cursor: WindowCursor, + pub cursor_position: WindowCursorPosition, + pub handle: WindowHandle, + pub presentation: WindowPresentation, + pub mode: WindowModeComponent, + pub position: WindowPosition, + pub resolution: WindowResolution, + pub title: WindowTitle, + pub canvas: WindowCanvas, + pub resize_constraints: WindowResizeConstraints, + pub focused: WindowCurrentlyFocused, } /// The size limits on a window. @@ -152,6 +152,14 @@ pub struct WindowCursor { } impl WindowCursor { + pub fn new(cursor_icon: CursorIcon, cursor_visible: bool, cursor_locked: bool) -> Self { + Self { + cursor_icon, + cursor_visible, + cursor_locked, + } + } + #[inline] pub fn cursor_icon(&self) -> CursorIcon { self.cursor_icon @@ -183,11 +191,17 @@ impl WindowCursor { #[derive(Component)] pub struct WindowCursorPosition { // TODO: Docs - // This is None if the cursor has left the window + /// This is None if the cursor has left the window physical_cursor_position: Option, } impl WindowCursorPosition { + pub fn new(physical_cursor_position: Option) -> Self { + Self { + physical_cursor_position, + } + } + /// The current mouse position, in physical pixels. #[inline] pub fn physical_cursor_position(&self) -> Option { @@ -209,6 +223,12 @@ pub struct WindowHandle { } impl WindowHandle { + pub fn new(raw_window_handle: RawWindowHandle) -> Self { + Self { + raw_window_handle: RawWindowHandleWrapper::new(raw_window_handle), + } + } + pub fn raw_window_handle(&self) -> RawWindowHandleWrapper { self.raw_window_handle.clone() } @@ -221,6 +241,10 @@ pub struct WindowPresentation { } impl WindowPresentation { + pub fn new(present_mode: PresentMode) -> Self { + Self { present_mode } + } + #[inline] #[doc(alias = "vsync")] pub fn present_mode(&self) -> PresentMode { @@ -239,6 +263,10 @@ pub struct WindowModeComponent { } impl WindowModeComponent { + pub fn new(mode: WindowMode) -> Self { + Self { mode } + } + #[inline] pub fn mode(&self) -> WindowMode { self.mode @@ -251,10 +279,15 @@ impl WindowModeComponent { #[derive(Component)] pub struct WindowPosition { + // TODO: Document why this must be option position: Option, } impl WindowPosition { + pub fn new(position: Option) -> Self { + Self { position } + } + /// The window's client position in physical pixels. #[inline] pub fn position(&self) -> Option { @@ -293,6 +326,24 @@ pub struct WindowResolution { } impl WindowResolution { + pub fn new( + requested_width: f32, + requested_height: f32, + physical_width: u32, + physical_height: u32, + scale_factor_override: Option, + backend_scale_factor: f64, + ) -> Self { + Self { + requested_width, + requested_height, + physical_width, + physical_height, + scale_factor_override, + backend_scale_factor, + } + } + /// The ratio of physical pixels to logical pixels /// /// `physical_pixels = logical_pixels * scale_factor` @@ -379,6 +430,10 @@ pub struct WindowTitle { } impl WindowTitle { + pub fn new(title: String) -> Self { + Self { title } + } + #[inline] pub fn title(&self) -> &str { &self.title @@ -414,6 +469,13 @@ pub struct WindowCanvas { } impl WindowCanvas { + pub fn new(canvas: Option, fit_canvas_to_parent: bool) -> Self { + Self { + canvas, + fit_canvas_to_parent, + } + } + /// The "html canvas" element selector. If set, this selector will be used to find a matching html canvas element, /// rather than creating a new one. /// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector). diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index fa5f7faa32c27..fd5f9870d5bf7 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -2,19 +2,22 @@ use bevy_ecs::{ entity::Entity, event::{EventReader, EventWriter}, prelude::{Added, With}, - system::{Commands, NonSendMut, Query, RemovedComponents}, schedule::IntoRunCriteria, + schedule::IntoRunCriteria, + system::{Commands, NonSendMut, Query, RemovedComponents}, }; use bevy_math::IVec2; use bevy_utils::tracing::error; use bevy_window::{ - CloseWindowCommand, CreateWindow, SetCursorIconCommand, SetCursorLockModeCommand, - SetCursorPositionCommand, SetCursorVisibilityCommand, SetDecorationsCommand, - SetMaximizedCommand, SetMinimizedCommand, SetPositionCommand, SetPresentModeCommand, - SetResizableCommand, SetResizeConstraintsCommand, SetResolutionCommand, SetScaleFactorCommand, - SetTitleCommand, SetWindowModeCommand, Window, WindowBundle, WindowClosed, WindowCreated, - WindowCursor, WindowCursorPosition, WindowDecorated, WindowMaximized, WindowMinimized, - WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, WindowResolution, - WindowScaleFactorChanged, WindowTitle, WindowTransparent, CursorIcon, WindowCurrentlyFocused, PresentMode, WindowHandle, RawWindowHandleWrapper, WindowCanvas, WindowResizeConstraints, + CloseWindowCommand, CreateWindow, CursorIcon, PresentMode, RawWindowHandleWrapper, + SetCursorIconCommand, SetCursorLockModeCommand, SetCursorPositionCommand, + SetCursorVisibilityCommand, SetDecorationsCommand, SetMaximizedCommand, SetMinimizedCommand, + SetPositionCommand, SetPresentModeCommand, SetResizableCommand, SetResizeConstraintsCommand, + SetResolutionCommand, SetScaleFactorCommand, SetTitleCommand, SetWindowModeCommand, Window, + WindowBundle, WindowCanvas, WindowClosed, WindowCreated, WindowCurrentlyFocused, WindowCursor, + WindowCursorPosition, WindowDecorated, WindowHandle, WindowMaximized, WindowMinimized, + WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, + WindowResizeConstraints, WindowResolution, WindowScaleFactorChanged, WindowTitle, + WindowTransparent, }; use raw_window_handle::HasRawWindowHandle; use winit::{ @@ -42,41 +45,39 @@ pub(crate) fn create_windows( // Prepare data let position = winit_window - .outer_position() - .ok() - .map(|position| IVec2::new(position.x, position.y)); + .outer_position() + .ok() + .map(|position| IVec2::new(position.x, position.y)); let inner_size = winit_window.inner_size(); entity_commands.insert_bundle(WindowBundle { window: Window, - handle: WindowHandle { raw_window_handle: RawWindowHandleWrapper::new(winit_window.raw_window_handle()) }, - presentation: WindowPresentation { present_mode: event.descriptor.present_mode }, - mode: WindowModeComponent { mode: event.descriptor.mode }, - position: WindowPosition { position }, - resolution: WindowResolution { - requested_width: event.descriptor.width, - requested_height: event.descriptor.height, - physical_width: inner_size.width, - physical_height: inner_size.height, - scale_factor_override: event.descriptor.scale_factor_override, - backend_scale_factor: winit_window.scale_factor(), - }, - title: WindowTitle { - title: event.descriptor.title.clone(), - }, - cursor_position: WindowCursorPosition { physical_cursor_position: None }, - cursor: WindowCursor { - cursor_icon: CursorIcon::Default, - cursor_visible: event.descriptor.cursor_visible, - cursor_locked: event.descriptor.cursor_locked, - }, - canvas: WindowCanvas { - canvas: event.descriptor.canvas.clone(), - fit_canvas_to_parent: event.descriptor.fit_canvas_to_parent - }, + handle: WindowHandle::new(winit_window.raw_window_handle()), + presentation: WindowPresentation::new(event.descriptor.present_mode), + mode: WindowModeComponent::new(event.descriptor.mode), + position: WindowPosition::new(position), + resolution: WindowResolution::new( + event.descriptor.width, + event.descriptor.height, + inner_size.width, + inner_size.height, + event.descriptor.scale_factor_override, + winit_window.scale_factor(), + ), + title: WindowTitle::new(event.descriptor.title.clone()), + cursor_position: WindowCursorPosition::new(None), + cursor: WindowCursor::new( + CursorIcon::Default, + event.descriptor.cursor_visible, + event.descriptor.cursor_locked, + ), + canvas: WindowCanvas::new( + event.descriptor.canvas.clone(), + event.descriptor.fit_canvas_to_parent, + ), resize_constraints: event.descriptor.resize_constraints, - // TODO: All new windows must be focused? - focused: WindowCurrentlyFocused, + // TODO: Are newly created windows considered focused by default? + focused: WindowCurrentlyFocused, }); // Optional marker components From c97365a22369a92cbfaadcee45797cac4b8beabe Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 13:59:50 +0200 Subject: [PATCH 19/32] cleanup --- crates/bevy_render/src/camera/camera.rs | 13 +++++---- crates/bevy_render/src/view/window.rs | 36 +++++++++++++++---------- crates/bevy_window/src/lib.rs | 2 +- crates/bevy_winit/src/winit_windows.rs | 6 +++-- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 40a18e0be7651..7978bc0c2e61c 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -12,15 +12,16 @@ use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, + prelude::With, query::Added, reflect::ReflectComponent, - system::{Commands, ParamSet, Query, Res}, prelude::With, + system::{Commands, ParamSet, Query, Res}, }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; -use bevy_window::{WindowCreated, WindowResized, Window, WindowResolution}; +use bevy_window::{Window, WindowCreated, WindowResized, WindowResolution}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, ops::Range}; use wgpu::Extent3d; @@ -229,7 +230,7 @@ pub enum RenderTarget { impl Default for RenderTarget { // TODO: Default window ID no longer makes sense, so this can no longer impl Default fn default() -> Self { - // TODO: + // TODO: // There is no longer 1 fixed id that corresponds to a valid window // This should probably be the Entity of the PrimaryWindow // but that is generated in runtime @@ -263,14 +264,16 @@ impl RenderTarget { RenderTarget::Window(window_id) => { if let Ok((entity, resolution)) = windows.get(*window_id) { RenderTargetInfo { - physical_size: UVec2::new(resolution.physical_width(), resolution.physical_height()), + physical_size: UVec2::new( + resolution.physical_width(), + resolution.physical_height(), + ), scale_factor: resolution.scale_factor(), } } else { // TODO: Helpful panic comment panic!("Render target does not point to a valid window"); } - } RenderTarget::Image(image_handle) => { let image = images.get(image_handle)?; diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 1be935d27ce91..b93082de3dd86 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -7,7 +7,10 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; -use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, Window, WindowResolution, WindowHandle, WindowPresentation }; +use bevy_window::{ + PresentMode, RawWindowHandleWrapper, Window, WindowClosed, WindowHandle, WindowPresentation, + WindowResolution, +}; use std::ops::{Deref, DerefMut}; use wgpu::TextureFormat; @@ -70,7 +73,15 @@ impl DerefMut for ExtractedWindows { fn extract_windows( mut render_world: ResMut, mut closed: EventReader, - windows: Query<(Entity, &WindowResolution, &WindowHandle, &WindowPresentation), With> + windows: Query< + ( + Entity, + &WindowResolution, + &WindowHandle, + &WindowPresentation, + ), + With, + >, ) { let mut extracted_windows = render_world.get_resource_mut::().unwrap(); for (entity, resolution, handle, presentation) in windows.iter() { @@ -79,18 +90,15 @@ fn extract_windows( resolution.physical_height().max(1), ); - let mut extracted_window = - extracted_windows - .entry(entity) - .or_insert(ExtractedWindow { - id: entity, - handle: handle.raw_window_handle(), - physical_width: new_width, - physical_height: new_height, - present_mode: presentation.present_mode(), - swap_chain_texture: None, - size_changed: false, - }); + let mut extracted_window = extracted_windows.entry(entity).or_insert(ExtractedWindow { + id: entity, + handle: handle.raw_window_handle(), + physical_width: new_width, + physical_height: new_height, + present_mode: presentation.present_mode(), + swap_chain_texture: None, + size_changed: false, + }); // NOTE: Drop the swap chain frame here extracted_window.swap_chain_texture = None; diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 67af213a7cc94..b4ada7ee65468 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -144,7 +144,7 @@ pub enum ExitCondition { /// Resource containing the Entity that is currently considered the primary window pub struct PrimaryWindow { - // TODO: + // TODO: // Should this be Option? // should this be allowed to change? // If yes, what should be responsible for updating it? diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 093c9e4059ff5..346b920572aa1 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -154,10 +154,12 @@ impl WinitWindows { // TODO: Might be more elegant ways to get return the reference of the winit-window let id = winit_window.id(); self.windows.insert(winit_window.id(), winit_window); - let created_window = self.windows.get(&id).expect("Winit should alway have the window it just created"); + let created_window = self + .windows + .get(&id) + .expect("Winit should alway have the window it just created"); created_window - } // TODO: Docs From 0e05569ed84accb68b934da83d017b256d596016 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 14:51:08 +0200 Subject: [PATCH 20/32] Start attempting to get rendering to compile again --- crates/bevy_render/src/camera/camera.rs | 54 ++++++++++++++++--------- crates/bevy_render/src/lib.rs | 39 ++++++++++++++---- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 7978bc0c2e61c..b1bdf9f3629b6 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -20,7 +20,7 @@ use bevy_ecs::{ use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_transform::components::GlobalTransform; -use bevy_utils::HashSet; +use bevy_utils::{default, HashSet}; use bevy_window::{Window, WindowCreated, WindowResized, WindowResolution}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, ops::Range}; @@ -71,6 +71,7 @@ pub struct ComputedCameraValues { target_info: Option, } +// TODO: Need to appease Reflect + default #[derive(Component, Debug, Reflect, Clone)] #[reflect(Component)] pub struct Camera { @@ -91,20 +92,31 @@ pub struct Camera { pub target: RenderTarget, } -impl Default for Camera { - fn default() -> Self { +// impl Default for Camera { +// fn default() -> Self { +// Self { +// is_active: true, +// priority: 0, +// viewport: None, +// computed: Default::default(), +// target: Default::default(), // TODO: Fix default of camera +// depth_calculation: Default::default(), +// } +// } +// } + +impl Camera { + pub fn from_render_target(target: RenderTarget) -> Self { Self { - is_active: true, - priority: 0, viewport: None, - computed: Default::default(), - target: Default::default(), // TODO: Fix default of camera + priority: 0, + is_active: true, depth_calculation: Default::default(), + computed: Default::default(), + target, } } -} -impl Camera { /// The logical size of this camera's viewport. If the `viewport` field is set to [`Some`], this /// will be the size of that custom viewport. Otherwise it will default to the full logical size of /// the current [`RenderTarget`]. @@ -227,18 +239,24 @@ pub enum RenderTarget { Image(Handle), } -impl Default for RenderTarget { - // TODO: Default window ID no longer makes sense, so this can no longer impl Default - fn default() -> Self { - // TODO: - // There is no longer 1 fixed id that corresponds to a valid window - // This should probably be the Entity of the PrimaryWindow - // but that is generated in runtime - // so not quite sure how to approach this - Self::Window(Default::default()) +impl RenderTarget { + pub fn new(window: Entity) -> Self { + Self::Window(window) } } +// impl Default for RenderTarget { +// // TODO: Default window ID no longer makes sense, so this can no longer impl Default +// fn default() -> Self { +// // TODO: +// // There is no longer 1 fixed id that corresponds to a valid window +// // This should probably be the Entity of the PrimaryWindow +// // but that is generated in runtime +// // so not quite sure how to approach this +// Self::Window(Default::default()) +// } +// } + impl RenderTarget { pub fn get_texture_view<'a>( &self, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index c700002279043..1f40691330de2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -27,6 +27,7 @@ pub mod prelude { }; } +use bevy_window::{PrimaryWindow, Window, WindowHandle}; pub use once_cell; use crate::{ @@ -42,8 +43,9 @@ use crate::{ }; use bevy_app::{App, AppLabel, Plugin}; use bevy_asset::{AddAsset, AssetServer}; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, system::SystemState}; use bevy_utils::tracing::debug; +use core::panic; use std::ops::{Deref, DerefMut}; /// Contains the default Bevy rendering backend based on wgpu. @@ -114,6 +116,12 @@ struct ScratchRenderWorld(World); impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { + let mut system_state: SystemState<( + Query<&WindowHandle, With>, + Res, + )> = SystemState::new(&mut app.world); + let (window_query, primary_window) = system_state.get(&mut app.world); + let options = app .world .get_resource::() @@ -129,13 +137,28 @@ impl Plugin for RenderPlugin { if let Some(backends) = options.backends { let instance = wgpu::Instance::new(backends); let surface = { - // TODO: This must be a query - let windows = app.world.resource_mut::(); - let raw_handle = windows.get_primary().map(|window| unsafe { - let handle = window.raw_window_handle().get_handle(); - instance.create_surface(&handle) - }); - raw_handle + // TODO: This can probably be cleaner + if let Ok(handle_component) = window_query.get( + primary_window + .window + .expect("There should be a primary window"), + ) { + // TODO: Make sure this is ok + unsafe { + let handle = handle_component.raw_window_handle().get_handle(); + instance.create_surface(&handle) + } + } else { + // TODO: Helpful panic comment + panic!("No WindowHandle component on primary window"); + } + // let handle_component = + // // let windows = app.world.resource_mut::(); + // let raw_handle = windows.get_primary().map(|window| unsafe { + // let handle = window.raw_window_handle().get_handle(); + // instance.create_surface(&handle) + // }); + // raw_handle }; let request_adapter_options = wgpu::RequestAdapterOptions { power_preference: options.power_preference, From d852a55795dc2cec6ea38b81e2652b0276ab99d5 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 18:32:46 +0200 Subject: [PATCH 21/32] Updating `bevy_text` --- crates/bevy_text/src/text2d.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index d0bebbad3609a..122b055a378d8 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ event::EventReader, query::Changed, reflect::ReflectComponent, - system::{Local, Query, Res, ResMut}, + system::{Local, Query, Res, ResMut}, prelude::With, }; use bevy_math::{Vec2, Vec3}; use bevy_reflect::Reflect; @@ -14,7 +14,7 @@ use bevy_render::{texture::Image, view::Visibility, RenderWorld}; use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas}; use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_utils::HashSet; -use bevy_window::{WindowId, WindowScaleFactorChanged, Windows}; +use bevy_window::{WindowScaleFactorChanged, PrimaryWindow, WindowResolution, Window}; use crate::{ DefaultTextPipeline, Font, FontAtlasSet, HorizontalAlign, Text, TextError, VerticalAlign, @@ -64,12 +64,14 @@ pub fn extract_text2d_sprite( mut render_world: ResMut, texture_atlases: Res>, text_pipeline: Res, - windows: Res, + primary_window: Res, + windows: Query<&WindowResolution, With>, text2d_query: Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>, ) { let mut extracted_sprites = render_world.resource_mut::(); - let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + let resolution = windows.get(primary_window.window.expect("Primary window should exist")).expect("Primary windows should have a valid WindowResolution component"); + let scale_factor = resolution.scale_factor() as f32; for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() { if !visibility.is_visible { @@ -133,7 +135,8 @@ pub fn update_text2d_layout( mut queue: Local>, mut textures: ResMut>, fonts: Res>, - windows: Res, + primary_window: Res, + windows: Query<&WindowResolution, With>, mut scale_factor_changed: EventReader, mut texture_atlases: ResMut>, mut font_atlas_set_storage: ResMut>, @@ -148,7 +151,9 @@ pub fn update_text2d_layout( ) { // We need to consume the entire iterator, hence `last` let factor_changed = scale_factor_changed.iter().last().is_some(); - let scale_factor = windows.scale_factor(WindowId::primary()); + + let resolution = windows.get(primary_window.window.expect("Primary window should exist")).expect("Primary windows should have a valid WindowResolution component"); + let scale_factor = resolution.scale_factor(); for (entity, text_changed, text, maybe_bounds, mut calculated_size) in text_query.iter_mut() { if factor_changed || text_changed || queue.remove(&entity) { From dc6875c96d3c6358bc9999a14b3e930e29e7d461 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 19:54:02 +0200 Subject: [PATCH 22/32] Fixing up `bevy_ui` --- crates/bevy_ui/src/flex/mod.rs | 30 ++++++++++++++++-------------- crates/bevy_ui/src/focus.rs | 17 +++++++++++------ crates/bevy_ui/src/render/mod.rs | 8 +++++--- crates/bevy_ui/src/widget/text.rs | 8 +++++--- 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 607daaacbc26a..38ce3daa53d40 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -12,13 +12,13 @@ use bevy_log::warn; use bevy_math::Vec2; use bevy_transform::components::Transform; use bevy_utils::HashMap; -use bevy_window::{Window, WindowId, WindowScaleFactorChanged, Windows}; +use bevy_window::{Window, WindowScaleFactorChanged, PrimaryWindow, WindowResolution}; use std::fmt; use stretch::{number::Number, Stretch}; pub struct FlexSurface { entity_to_stretch: HashMap, - window_nodes: HashMap, + window_nodes: HashMap, stretch: Stretch, } @@ -31,7 +31,7 @@ unsafe impl Sync for FlexSurface {} fn _assert_send_sync_flex_surface_impl_safe() { fn _assert_send_sync() {} _assert_send_sync::>(); - _assert_send_sync::>(); + _assert_send_sync::>(); // FIXME https://github.com/vislyhq/stretch/issues/69 // _assert_send_sync::(); } @@ -133,9 +133,9 @@ without UI components as a child of an entity with UI components, results may be .unwrap(); } - pub fn update_window(&mut self, window: &Window) { + pub fn update_window(&mut self, window_id: Entity, window_resolution: &WindowResolution) { let stretch = &mut self.stretch; - let node = self.window_nodes.entry(window.id()).or_insert_with(|| { + let node = self.window_nodes.entry(window_id).or_insert_with(|| { stretch .new_node(stretch::style::Style::default(), Vec::new()) .unwrap() @@ -146,8 +146,8 @@ without UI components as a child of an entity with UI components, results may be *node, stretch::style::Style { size: stretch::geometry::Size { - width: stretch::style::Dimension::Points(window.physical_width() as f32), - height: stretch::style::Dimension::Points(window.physical_height() as f32), + width: stretch::style::Dimension::Points(window_resolution.physical_width() as f32), + height: stretch::style::Dimension::Points(window_resolution.physical_height() as f32), }, ..Default::default() }, @@ -157,7 +157,7 @@ without UI components as a child of an entity with UI components, results may be pub fn set_window_children( &mut self, - window_id: WindowId, + window_id: Entity, children: impl Iterator, ) { let stretch_node = self.window_nodes.get(&window_id).unwrap(); @@ -200,7 +200,8 @@ pub enum FlexError { #[allow(clippy::too_many_arguments)] pub fn flex_node_system( - windows: Res, + primary_window: Res, + windows: Query<(Entity, &WindowResolution), With>, mut scale_factor_events: EventReader, mut flex_surface: ResMut, root_node_query: Query, Without)>, @@ -214,12 +215,13 @@ pub fn flex_node_system( mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>, ) { // update window root nodes - for window in windows.iter() { - flex_surface.update_window(window); + for (window_id, window_resolution) in windows.iter() { + flex_surface.update_window(window_id, window_resolution); } // assume one window for time being... - let logical_to_physical_factor = windows.scale_factor(WindowId::primary()); + let (_, primary_resolution) = windows.get(primary_window.window.expect("Primary window should exist")).expect("Primary windows should have a valid WindowResolution component"); + let logical_to_physical_factor = primary_resolution.scale_factor(); if scale_factor_events.iter().next_back().is_some() { update_changed( @@ -254,8 +256,8 @@ pub fn flex_node_system( // TODO: handle removed nodes // update window children (for now assuming all Nodes live in the primary window) - if let Some(primary_window) = windows.get_primary() { - flex_surface.set_window_children(primary_window.id(), root_node_query.iter()); + if let Some(primary_window_id) = primary_window.window { + flex_surface.set_window_children(primary_window_id, root_node_query.iter()); } // update children diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 03a269c690dca..f7ba3d372955c 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -1,7 +1,7 @@ use crate::{CalculatedClip, Node}; use bevy_ecs::{ entity::Entity, - prelude::Component, + prelude::{Component, With}, reflect::ReflectComponent, system::{Local, Query, Res}, }; @@ -10,7 +10,7 @@ use bevy_math::Vec2; use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; -use bevy_window::Windows; +use bevy_window::{PrimaryWindow, WindowCursorPosition, Window}; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; @@ -59,7 +59,8 @@ pub struct State { /// The system that sets Interaction for all UI elements based on the mouse cursor activity pub fn ui_focus_system( mut state: Local, - windows: Res, + primary_window: Res, + cursor_positions: Query<&WindowCursorPosition, With>, mouse_button_input: Res>, touches_input: Res, mut node_query: Query<( @@ -71,9 +72,13 @@ pub fn ui_focus_system( Option<&CalculatedClip>, )>, ) { - let cursor_position = windows - .get_primary() - .and_then(|window| window.cursor_position()); + let primary_window_id = primary_window + .window + .expect("Primary window should exist"); + // Cursor position of primary window + let cursor_position = *cursor_positions + .get(primary_window_id) + .expect("Primary window should have a valid WindowCursorPosition component"); // reset entities that were both clicked and released in the last frame for entity in state.entities_to_reset.drain(..) { diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index b4bcb94e0a0c8..5c3d78b0bfc39 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,6 +2,7 @@ mod pipeline; mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; +use bevy_window::{PrimaryWindow, WindowResolution, Window}; pub use pipeline::*; pub use render_pass::*; @@ -28,7 +29,6 @@ use bevy_text::{DefaultTextPipeline, Text}; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; use bevy_utils::HashMap; -use bevy_window::{WindowId, Windows}; use bytemuck::{Pod, Zeroable}; use std::ops::Range; @@ -274,7 +274,8 @@ pub fn extract_text_uinodes( mut render_world: ResMut, texture_atlases: Res>, text_pipeline: Res, - windows: Res, + primary_window: Res, + windows: Query<&WindowResolution, With>, uinode_query: Query<( Entity, &Node, @@ -286,7 +287,8 @@ pub fn extract_text_uinodes( ) { let mut extracted_uinodes = render_world.resource_mut::(); - let scale_factor = windows.scale_factor(WindowId::primary()) as f32; + let resolution = windows.get(primary_window.window.expect("Primary window should exist")).expect("Primary windows should have a valid WindowResolution component"); + let scale_factor = resolution.scale_factor() as f32; for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() { if !visibility.is_visible { diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 895c4a6fdee48..c99525efc23f1 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -9,7 +9,7 @@ use bevy_math::Vec2; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; use bevy_text::{DefaultTextPipeline, Font, FontAtlasSet, Text, TextError}; -use bevy_window::{WindowId, Windows}; +use bevy_window::{PrimaryWindow, WindowResolution, Window}; #[derive(Debug, Default)] pub struct QueuedText { @@ -42,7 +42,8 @@ pub fn text_system( mut last_scale_factor: Local, mut textures: ResMut>, fonts: Res>, - windows: Res, + primary_window: Res, + windows: Query<&WindowResolution, With>, mut texture_atlases: ResMut>, mut font_atlas_set_storage: ResMut>, mut text_pipeline: ResMut, @@ -52,7 +53,8 @@ pub fn text_system( Query<(&Text, &Style, &mut CalculatedSize)>, )>, ) { - let scale_factor = windows.scale_factor(WindowId::primary()); + let resolution = windows.get(primary_window.window.expect("Primary window should exist")).expect("Primary windows should have a valid WindowResolution component"); + let scale_factor = resolution.scale_factor(); let inv_scale_factor = 1. / scale_factor; From 0696336a8a1e09b787c0457742aba633146246e9 Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 19:54:17 +0200 Subject: [PATCH 23/32] Cleanup --- crates/bevy_render/src/camera/camera.rs | 64 +++++++++---------------- crates/bevy_render/src/lib.rs | 33 ++++++++----- crates/bevy_window/src/system.rs | 2 +- crates/bevy_winit/src/lib.rs | 4 +- crates/bevy_winit/src/system.rs | 10 +--- 5 files changed, 48 insertions(+), 65 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index b1bdf9f3629b6..54f09cdec64db 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -12,10 +12,9 @@ use bevy_ecs::{ component::Component, entity::Entity, event::EventReader, - prelude::With, query::Added, reflect::ReflectComponent, - system::{Commands, ParamSet, Query, Res}, + system::{Commands, ParamSet, Query, Res}, prelude::With, }; use bevy_math::{Mat4, UVec2, Vec2, Vec3}; use bevy_reflect::prelude::*; @@ -71,7 +70,6 @@ pub struct ComputedCameraValues { target_info: Option, } -// TODO: Need to appease Reflect + default #[derive(Component, Debug, Reflect, Clone)] #[reflect(Component)] pub struct Camera { @@ -92,30 +90,20 @@ pub struct Camera { pub target: RenderTarget, } -// impl Default for Camera { -// fn default() -> Self { -// Self { -// is_active: true, -// priority: 0, -// viewport: None, -// computed: Default::default(), -// target: Default::default(), // TODO: Fix default of camera -// depth_calculation: Default::default(), -// } -// } -// } - -impl Camera { - pub fn from_render_target(target: RenderTarget) -> Self { +impl Default for Camera { + fn default() -> Self { Self { - viewport: None, - priority: 0, is_active: true, - depth_calculation: Default::default(), + priority: 0, + viewport: None, computed: Default::default(), - target, + target: RenderTarget::PrimaryWindow, + depth_calculation: Default::default(), } } +} + +impl Camera { /// The logical size of this camera's viewport. If the `viewport` field is set to [`Some`], this /// will be the size of that custom viewport. Otherwise it will default to the full logical size of @@ -233,30 +221,20 @@ impl CameraRenderGraph { /// swapchain or an [`Image`]. #[derive(Debug, Clone, Reflect, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum RenderTarget { + /// Renders the camera's view to the current primary window + PrimaryWindow, /// Window to which the camera's view is rendered. Window(Entity), /// Image to which the camera's view is rendered. Image(Handle), } -impl RenderTarget { - pub fn new(window: Entity) -> Self { - Self::Window(window) +impl Default for RenderTarget { + fn default() -> Self { + Self::PrimaryWindow } } -// impl Default for RenderTarget { -// // TODO: Default window ID no longer makes sense, so this can no longer impl Default -// fn default() -> Self { -// // TODO: -// // There is no longer 1 fixed id that corresponds to a valid window -// // This should probably be the Entity of the PrimaryWindow -// // but that is generated in runtime -// // so not quite sure how to approach this -// Self::Window(Default::default()) -// } -// } - impl RenderTarget { pub fn get_texture_view<'a>( &self, @@ -270,17 +248,18 @@ impl RenderTarget { RenderTarget::Image(image_handle) => { images.get(image_handle).map(|image| &image.texture_view) } + RenderTarget::PrimaryWindow => todo!(), } } pub fn get_render_target_info( &self, - windows: Query<(Entity, &WindowResolution), With>, // TODO: Maybe this could just be a Vec? + windows: &Query<(&WindowResolution), With>, // TODO: Maybe this could just be a Vec? images: &Assets, ) -> Option { Some(match self { RenderTarget::Window(window_id) => { - if let Ok((entity, resolution)) = windows.get(*window_id) { + if let Ok(resolution) = windows.get(*window_id) { RenderTargetInfo { physical_size: UVec2::new( resolution.physical_width(), @@ -301,8 +280,10 @@ impl RenderTarget { scale_factor: 1.0, } } + RenderTarget::PrimaryWindow => todo!(), }) } + // Check if this render target is contained in the given changed windows or images. fn is_changed( &self, @@ -312,6 +293,7 @@ impl RenderTarget { match self { RenderTarget::Window(window_id) => changed_window_ids.contains(window_id), RenderTarget::Image(image_handle) => changed_image_handles.contains(&image_handle), + RenderTarget::PrimaryWindow => todo!(), } } } @@ -335,7 +317,7 @@ pub fn camera_system( mut window_resized_events: EventReader, mut window_created_events: EventReader, mut image_asset_events: EventReader>, - windows: Query<(Entity, &WindowResolution), With>, + windows: Query<&WindowResolution, With>, images: Res>, mut queries: ParamSet<( Query<(Entity, &mut Camera, &mut T)>, @@ -385,7 +367,7 @@ pub fn camera_system( || added_cameras.contains(&entity) || camera_projection.is_changed() { - camera.computed.target_info = camera.target.get_render_target_info(windows, &images); + camera.computed.target_info = camera.target.get_render_target_info(&windows, &images); if let Some(size) = camera.logical_viewport_size() { camera_projection.update(size.x, size.y); camera.computed.projection_matrix = camera_projection.get_projection_matrix(); diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 1f40691330de2..c652a44a79b4b 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -29,6 +29,7 @@ pub mod prelude { use bevy_window::{PrimaryWindow, Window, WindowHandle}; pub use once_cell; +use settings::WgpuSettings; use crate::{ camera::CameraPlugin, @@ -116,17 +117,6 @@ struct ScratchRenderWorld(World); impl Plugin for RenderPlugin { /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { - let mut system_state: SystemState<( - Query<&WindowHandle, With>, - Res, - )> = SystemState::new(&mut app.world); - let (window_query, primary_window) = system_state.get(&mut app.world); - - let options = app - .world - .get_resource::() - .cloned() - .unwrap_or_default(); app.add_asset::() .add_debug_asset::() @@ -134,6 +124,25 @@ impl Plugin for RenderPlugin { .init_debug_asset_loader::() .register_type::(); + let mut system_state: SystemState<( + Query<&WindowHandle, With>, + Res, + Res, + )> = SystemState::new(&mut app.world); + let ( + window_query, + primary_window, + options // This was .clone().unwrap_or_default(). Will this work the same? + ) = system_state.get(&mut app.world); + + // let options = app + // .world + // .get_resource::() + // .cloned() + // .unwrap_or_default(); + + + if let Some(backends) = options.backends { let instance = wgpu::Instance::new(backends); let surface = { @@ -146,7 +155,7 @@ impl Plugin for RenderPlugin { // TODO: Make sure this is ok unsafe { let handle = handle_component.raw_window_handle().get_handle(); - instance.create_surface(&handle) + Some(instance.create_surface(&handle)) } } else { // TODO: Helpful panic comment diff --git a/crates/bevy_window/src/system.rs b/crates/bevy_window/src/system.rs index b0e7b879b144b..819177c2f61a5 100644 --- a/crates/bevy_window/src/system.rs +++ b/crates/bevy_window/src/system.rs @@ -1,5 +1,5 @@ use crate::{ - window, PrimaryWindow, Window, WindowCloseRequested, WindowClosed, WindowCommandsExtension, + PrimaryWindow, Window, WindowCloseRequested, WindowClosed, WindowCommandsExtension, WindowCurrentlyFocused, WindowDescriptor, }; diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index c41ef7859fe4c..97d9944b22c33 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -23,9 +23,8 @@ pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Plugin}; use bevy_ecs::{ event::{Events, ManualEventReader}, - world::World, }; -use bevy_ecs::{prelude::*, world}; +use bevy_ecs::prelude::*; use bevy_input::{ keyboard::KeyboardInput, mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, @@ -45,7 +44,6 @@ use bevy_window::{ }; use winit::{ - dpi::{LogicalSize, PhysicalPosition}, event::{self, DeviceEvent, Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, }; diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index fd5f9870d5bf7..7d9e3842f1cb3 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -2,21 +2,19 @@ use bevy_ecs::{ entity::Entity, event::{EventReader, EventWriter}, prelude::{Added, With}, - schedule::IntoRunCriteria, system::{Commands, NonSendMut, Query, RemovedComponents}, }; use bevy_math::IVec2; use bevy_utils::tracing::error; use bevy_window::{ - CloseWindowCommand, CreateWindow, CursorIcon, PresentMode, RawWindowHandleWrapper, + CloseWindowCommand, CreateWindow, CursorIcon, SetCursorIconCommand, SetCursorLockModeCommand, SetCursorPositionCommand, SetCursorVisibilityCommand, SetDecorationsCommand, SetMaximizedCommand, SetMinimizedCommand, SetPositionCommand, SetPresentModeCommand, SetResizableCommand, SetResizeConstraintsCommand, SetResolutionCommand, SetScaleFactorCommand, SetTitleCommand, SetWindowModeCommand, Window, WindowBundle, WindowCanvas, WindowClosed, WindowCreated, WindowCurrentlyFocused, WindowCursor, WindowCursorPosition, WindowDecorated, WindowHandle, WindowMaximized, WindowMinimized, - WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, - WindowResizeConstraints, WindowResolution, WindowScaleFactorChanged, WindowTitle, + WindowModeComponent, WindowPosition, WindowPresentation, WindowResizable, WindowResolution, WindowScaleFactorChanged, WindowTitle, WindowTransparent, }; use raw_window_handle::HasRawWindowHandle; @@ -382,11 +380,9 @@ pub(crate) fn update_cursor_visibility( // TODO: Docs pub(crate) fn update_present_mode( mut components: Query<&mut WindowPresentation, With>, - winit_windows: NonSendMut, mut command_reader: EventReader, ) { for event in command_reader.iter() { - let winit_window = winit_windows.get_window(event.entity).unwrap(); // Update Winit // Present mode is only relevant for the renderer, so no need to do anything to Winit at this point @@ -405,11 +401,9 @@ pub(crate) fn update_present_mode( pub(crate) fn update_scale_factor( mut components: Query<&mut WindowResolution, With>, mut window_dpi_changed_events: EventWriter, - winit_windows: NonSendMut, mut command_reader: EventReader, ) { for event in command_reader.iter() { - let winit_window = winit_windows.get_window(event.entity).unwrap(); window_dpi_changed_events.send(WindowScaleFactorChanged { entity: event.entity, From aca87737fd96d7a7402b167514b22d9c250bd41b Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 20:19:17 +0200 Subject: [PATCH 24/32] Fixing up `bevy_ui` --- crates/bevy_ui/src/focus.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index f7ba3d372955c..41403115b5882 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -76,9 +76,10 @@ pub fn ui_focus_system( .window .expect("Primary window should exist"); // Cursor position of primary window - let cursor_position = *cursor_positions + let cursor_position = cursor_positions .get(primary_window_id) - .expect("Primary window should have a valid WindowCursorPosition component"); + .expect("Primary window should have a valid WindowCursorPosition component") + .physical_cursor_position(); // reset entities that were both clicked and released in the last frame for entity in state.entities_to_reset.drain(..) { @@ -120,8 +121,8 @@ pub fn ui_focus_system( // if the current cursor position is within the bounds of the node, consider it for // clicking let contains_cursor = if let Some(cursor_position) = cursor_position { - (min.x..max.x).contains(&cursor_position.x) - && (min.y..max.y).contains(&cursor_position.y) + (min.x..max.x).contains(&(cursor_position.x as f32)) + && (min.y..max.y).contains(&(cursor_position.y as f32)) } else { false }; From f6f793a460a33dab7833d70bac3056edfbbd42ed Mon Sep 17 00:00:00 2001 From: Andreas Weibye <13300393+Weibye@users.noreply.github.com> Date: Fri, 10 Jun 2022 23:29:41 +0200 Subject: [PATCH 25/32] fixing examples --- examples/3d/split_screen.rs | 11 +++--- examples/ecs/parallel_query.rs | 16 ++++++--- examples/games/contributors.rs | 17 ++++++---- examples/input/mouse_grab.rs | 15 +++++---- examples/ios/src/lib.rs | 14 +++++--- examples/shader/post_processing.rs | 14 +++++--- examples/stress_tests/bevymark.rs | 43 +++++++++++++++++------- examples/window/scale_factor_override.rs | 15 ++++++--- 8 files changed, 95 insertions(+), 50 deletions(-) diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index b296f170ddb48..2014c8f9df3af 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -4,7 +4,7 @@ use bevy::{ core_pipeline::clear_color::ClearColorConfig, prelude::*, render::camera::Viewport, - window::{WindowId, WindowResized}, + window::{WindowResized, PrimaryWindow, WindowResolution}, }; fn main() { @@ -79,17 +79,20 @@ struct LeftCamera; struct RightCamera; fn set_camera_viewports( - windows: Res, + primary_window: Res, + resolutions: Query<&WindowResolution, With>, mut resize_events: EventReader, mut left_camera: Query<&mut Camera, (With, Without)>, mut right_camera: Query<&mut Camera, With>, ) { + let primary_window_id = primary_window.window.expect("Should have a valid Primary window"); + // We need to dynamically resize the camera's viewports whenever the window size changes // so then each camera always takes up half the screen. // A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup. for resize_event in resize_events.iter() { - if resize_event.id == WindowId::primary() { - let window = windows.primary(); + if resize_event.entity == primary_window_id { + let window = resolutions.get(primary_window_id).expect("Primary window should have valid resolution"); let mut left_camera = left_camera.single_mut(); left_camera.viewport = Some(Viewport { physical_position: UVec2::new(0, 0), diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 2ecebd5e2a31c..7ca5d34cd080c 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -1,6 +1,6 @@ //! Illustrates parallel queries with `ParallelIterator`. -use bevy::prelude::*; +use bevy::{prelude::*, window::{WindowResolution, PrimaryWindow}}; use rand::random; #[derive(Component, Deref)] @@ -38,10 +38,16 @@ fn move_system(mut sprites: Query<(&mut Transform, &Velocity)>) { } // Bounce sprites outside the window -fn bounce_system(windows: Res, mut sprites: Query<(&Transform, &mut Velocity)>) { - let window = windows.primary(); - let width = window.width(); - let height = window.height(); +fn bounce_system( + primary_window: Res, + resolutions: Query<&WindowResolution, With>, + mut sprites: Query<(&Transform, &mut Velocity)> +) { + let resolution = resolutions + .get(primary_window.window.expect("Primary window should exist")) + .expect("Primary windows should have a valid WindowResolution component"); + let width = resolution.width(); + let height = resolution.height(); let left = width / -2.0; let right = width / 2.0; let bottom = height / -2.0; diff --git a/examples/games/contributors.rs b/examples/games/contributors.rs index ca0d513146235..5ff92e0d1a6d7 100644 --- a/examples/games/contributors.rs +++ b/examples/games/contributors.rs @@ -1,6 +1,6 @@ //! This example displays each contributor to the bevy source code as a bouncing bevy-ball. -use bevy::{prelude::*, utils::HashSet}; +use bevy::{prelude::*, utils::HashSet, window::{PrimaryWindow, WindowResolution}}; use rand::{prelude::SliceRandom, Rng}; use std::{ env::VarError, @@ -253,18 +253,21 @@ fn velocity_system(time: Res