From 7d6f900336c5795ed89f334f73981f93d2186c6c Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Sun, 12 Jun 2022 23:49:53 +0200 Subject: [PATCH 1/4] Add option to center a window --- crates/bevy_window/src/lib.rs | 2 +- crates/bevy_window/src/window.rs | 31 +++++++++++--- crates/bevy_winit/src/lib.rs | 15 +++++++ crates/bevy_winit/src/winit_windows.rs | 59 +++++++++++++++++--------- 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index ca57cde6fac6b..3e5a7223f2b21 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -17,7 +17,7 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, - Window, WindowDescriptor, WindowMoved, Windows, + Window, WindowDescriptor, WindowMoved, WindowPosition, Windows, }; } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 6557d414c590d..8684cd9fedf3a 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -259,6 +259,8 @@ pub enum WindowCommand { SetPosition { position: IVec2, }, + /// Modifies the position of the window to be in the center of the current monitor + Center, /// Set the window's [`WindowResizeConstraints`] SetResizeConstraints { resize_constraints: WindowResizeConstraints, @@ -416,6 +418,16 @@ impl Window { .push(WindowCommand::SetPosition { position }); } + /// Modifies the position of the window to be in the center of the current monitor + /// + /// # Platform-specific + /// - iOS: Can only be called on the main thread. + /// - Web / Android / Wayland: Unsupported. + #[inline] + pub fn center_window(&mut self) { + self.command_queue.push(WindowCommand::Center); + } + /// Modifies the minimum and maximum window bounds for resizing in logical pixels. #[inline] pub fn set_resize_constraints(&mut self, resize_constraints: WindowResizeConstraints) { @@ -714,6 +726,17 @@ impl Window { } } +/// Defines where window should be placed at on creation. +#[derive(Debug, Clone)] +pub enum WindowPosition { + /// Position will be set by window manager + Default, + /// Window will be centered at primary monitor + Centered, + /// Window will be placed at specified position + At(Vec2), +} + /// Describes the information needed for creating a window. /// /// This should be set up before adding the [`WindowPlugin`](crate::WindowPlugin). @@ -732,10 +755,8 @@ pub struct WindowDescriptor { /// /// May vary from the physical height due to different pixel density on different monitors. pub height: f32, - /// The position on the screen that the window will be centered at. - /// - /// If set to `None`, some platform-specific position will be chosen. - pub position: Option, + /// The position on the screen that the window will be placed at. + pub position: WindowPosition, /// Sets minimum and maximum resize limits. pub resize_constraints: WindowResizeConstraints, /// Overrides the window's ratio of physical pixels to logical pixels. @@ -799,7 +820,7 @@ impl Default for WindowDescriptor { title: "app".to_string(), width: 1280., height: 720., - position: None, + position: WindowPosition::Default, resize_constraints: WindowResizeConstraints::default(), scale_factor_override: None, present_mode: PresentMode::Fifo, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 88e3f81126bea..04475ff58465e 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -158,6 +158,21 @@ fn change_window( y: position[1], }); } + bevy_window::WindowCommand::Center => { + let window = winit_windows.get_window(id).unwrap(); + + // What to do if current_monitor is None? + // Abort? + // Or use primary_monitor? And then what if that also is None? + let screen_size = window.current_monitor().unwrap().size(); + + let window_size = window.outer_size(); + + window.set_outer_position(PhysicalPosition { + x: (screen_size.width - window_size.width) as f64 / 2., + y: (screen_size.height - window_size.height) as f64 / 2., + }); + } bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => { let window = winit_windows.get_window(id).unwrap(); let constraints = resize_constraints.check_constraints(); diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index e5565092701b7..74a374863d060 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -2,7 +2,7 @@ use bevy_math::IVec2; use bevy_utils::HashMap; use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; use raw_window_handle::HasRawWindowHandle; -use winit::dpi::LogicalSize; +use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition}; #[derive(Debug, Default)] pub struct WinitWindows { @@ -49,30 +49,47 @@ impl WinitWindows { .. } = window_descriptor; - if let Some(position) = position { - if let Some(sf) = scale_factor_override { - winit_window_builder = winit_window_builder.with_position( - winit::dpi::LogicalPosition::new( - position[0] as f64, - position[1] as f64, - ) - .to_physical::(*sf), - ); - } else { - winit_window_builder = - winit_window_builder.with_position(winit::dpi::LogicalPosition::new( - position[0] as f64, - position[1] as f64, - )); + use bevy_window::WindowPosition::*; + match position { + Default => { /* Window manager will handle position */ } + Centered => { + if let Some(monitor) = event_loop.primary_monitor() { + let screen_size = monitor.size(); + + let scale_factor = scale_factor_override.unwrap_or(1.0); + + // Logical to physical window size + let (width, height): (f64, f64) = LogicalSize::new(*width, *height) + .to_physical::(scale_factor) + .into(); + + let position = PhysicalPosition::new( + (screen_size.width as f64 - width) / 2., + (screen_size.height as f64 - height) / 2., + ); + + winit_window_builder = winit_window_builder.with_position(position); + } + } + At(position) => { + if let Some(sf) = scale_factor_override { + winit_window_builder = winit_window_builder.with_position( + LogicalPosition::new(position[0] as f64, position[1] as f64) + .to_physical::(*sf), + ); + } else { + winit_window_builder = winit_window_builder.with_position( + LogicalPosition::new(position[0] as f64, position[1] as f64), + ); + } } } + if let Some(sf) = scale_factor_override { - winit_window_builder.with_inner_size( - winit::dpi::LogicalSize::new(*width, *height).to_physical::(*sf), - ) - } else { winit_window_builder - .with_inner_size(winit::dpi::LogicalSize::new(*width, *height)) + .with_inner_size(LogicalSize::new(*width, *height).to_physical::(*sf)) + } else { + winit_window_builder.with_inner_size(LogicalSize::new(*width, *height)) } } .with_resizable(window_descriptor.resizable) From 96ef2b18b4ad3da9937d94a5c0e29c1545ba9e83 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Mon, 13 Jun 2022 19:10:52 +0000 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Alice Cecile --- crates/bevy_window/src/window.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8684cd9fedf3a..206aac8436a3a 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -730,8 +730,10 @@ impl Window { #[derive(Debug, Clone)] pub enum WindowPosition { /// Position will be set by window manager - Default, - /// Window will be centered at primary monitor + Automatic, + /// Window will be centered on the primary monitor + /// + /// Note that this does not account for window decorations. Centered, /// Window will be placed at specified position At(Vec2), From 655d747c825a4c6a8b3ccb945a994d6258534549 Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Mon, 13 Jun 2022 21:37:07 +0200 Subject: [PATCH 3/4] Improve documentation and naming --- crates/bevy_window/src/window.rs | 8 +++++--- crates/bevy_winit/src/winit_windows.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 206aac8436a3a..9dd4320b35938 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -729,13 +729,15 @@ impl Window { /// Defines where window should be placed at on creation. #[derive(Debug, Clone)] pub enum WindowPosition { - /// Position will be set by window manager + /// Position will be set by the window manager Automatic, /// Window will be centered on the primary monitor /// /// Note that this does not account for window decorations. Centered, - /// Window will be placed at specified position + /// The window's top-left corner will be placed at the specified position (in pixels) + /// + /// (0,0) represents top-left corner of screen space. At(Vec2), } @@ -822,7 +824,7 @@ impl Default for WindowDescriptor { title: "app".to_string(), width: 1280., height: 720., - position: WindowPosition::Default, + position: WindowPosition::Automatic, resize_constraints: WindowResizeConstraints::default(), scale_factor_override: None, present_mode: PresentMode::Fifo, diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 74a374863d060..a2fea74aa0d4d 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -51,7 +51,7 @@ impl WinitWindows { use bevy_window::WindowPosition::*; match position { - Default => { /* Window manager will handle position */ } + Automatic => { /* Window manager will handle position */ } Centered => { if let Some(monitor) = event_loop.primary_monitor() { let screen_size = monitor.size(); From 872bae78805f0d3fc99d24db8aae7478ce4ae10e Mon Sep 17 00:00:00 2001 From: LoipesMas <46327403+LoipesMas@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:33:14 +0200 Subject: [PATCH 4/4] Change window centering to use enum for monitor --- crates/bevy_window/src/lib.rs | 4 ++-- crates/bevy_window/src/window.rs | 24 ++++++++++++++----- crates/bevy_winit/src/lib.rs | 30 ++++++++++++++++-------- crates/bevy_winit/src/winit_windows.rs | 32 ++++++++++++++++++-------- 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 3e5a7223f2b21..ac43c01b08180 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -16,8 +16,8 @@ pub use windows::*; pub mod prelude { #[doc(hidden)] pub use crate::{ - CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter, - Window, WindowDescriptor, WindowMoved, WindowPosition, Windows, + CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, MonitorSelection, + ReceivedCharacter, Window, WindowDescriptor, WindowMoved, WindowPosition, Windows, }; } diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 9dd4320b35938..928c9ccf80eb5 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -260,7 +260,7 @@ pub enum WindowCommand { position: IVec2, }, /// Modifies the position of the window to be in the center of the current monitor - Center, + Center(MonitorSelection), /// Set the window's [`WindowResizeConstraints`] SetResizeConstraints { resize_constraints: WindowResizeConstraints, @@ -424,8 +424,9 @@ impl Window { /// - iOS: Can only be called on the main thread. /// - Web / Android / Wayland: Unsupported. #[inline] - pub fn center_window(&mut self) { - self.command_queue.push(WindowCommand::Center); + pub fn center_window(&mut self, monitor_selection: MonitorSelection) { + self.command_queue + .push(WindowCommand::Center(monitor_selection)); } /// Modifies the minimum and maximum window bounds for resizing in logical pixels. @@ -727,20 +728,31 @@ impl Window { } /// Defines where window should be placed at on creation. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum WindowPosition { /// Position will be set by the window manager Automatic, - /// Window will be centered on the primary monitor + /// Window will be centered on the selected monitor /// /// Note that this does not account for window decorations. - Centered, + Centered(MonitorSelection), /// The window's top-left corner will be placed at the specified position (in pixels) /// /// (0,0) represents top-left corner of screen space. At(Vec2), } +/// Defines which monitor to use. +#[derive(Debug, Clone, Copy)] +pub enum MonitorSelection { + /// Uses current monitor of the window. + Current, + /// Uses primary monitor of the system. + Primary, + /// Uses monitor with the specified index. + Number(usize), +} + /// Describes the information needed for creating a window. /// /// This should be set up before adding the [`WindowPlugin`](crate::WindowPlugin). diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 04475ff58465e..ead83c7eb4fcc 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -158,20 +158,30 @@ fn change_window( y: position[1], }); } - bevy_window::WindowCommand::Center => { + bevy_window::WindowCommand::Center(monitor_selection) => { let window = winit_windows.get_window(id).unwrap(); - // What to do if current_monitor is None? - // Abort? - // Or use primary_monitor? And then what if that also is None? - let screen_size = window.current_monitor().unwrap().size(); + use bevy_window::MonitorSelection::*; + let maybe_monitor = match monitor_selection { + Current => window.current_monitor(), + Primary => window.primary_monitor(), + Number(n) => window.available_monitors().nth(n), + }; - let window_size = window.outer_size(); + if let Some(monitor) = maybe_monitor { + let screen_size = monitor.size(); - window.set_outer_position(PhysicalPosition { - x: (screen_size.width - window_size.width) as f64 / 2., - y: (screen_size.height - window_size.height) as f64 / 2., - }); + let window_size = window.outer_size(); + + window.set_outer_position(PhysicalPosition { + x: screen_size.width.saturating_sub(window_size.width) as f64 / 2. + + monitor.position().x as f64, + y: screen_size.height.saturating_sub(window_size.height) as f64 / 2. + + monitor.position().y as f64, + }); + } else { + warn!("Couldn't get monitor selected with: {monitor_selection:?}"); + } } bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => { let window = winit_windows.get_window(id).unwrap(); diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index a2fea74aa0d4d..91550eeffecf7 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,5 +1,5 @@ use bevy_math::IVec2; -use bevy_utils::HashMap; +use bevy_utils::{tracing::warn, HashMap}; use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; use raw_window_handle::HasRawWindowHandle; use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition}; @@ -52,23 +52,37 @@ impl WinitWindows { use bevy_window::WindowPosition::*; match position { Automatic => { /* Window manager will handle position */ } - Centered => { - if let Some(monitor) = event_loop.primary_monitor() { + Centered(monitor_selection) => { + use bevy_window::MonitorSelection::*; + let maybe_monitor = match monitor_selection { + Current => { + warn!("Can't select current monitor on window creation!"); + None + } + Primary => event_loop.primary_monitor(), + Number(n) => event_loop.available_monitors().nth(*n), + }; + + if let Some(monitor) = maybe_monitor { let screen_size = monitor.size(); let scale_factor = scale_factor_override.unwrap_or(1.0); // Logical to physical window size - let (width, height): (f64, f64) = LogicalSize::new(*width, *height) - .to_physical::(scale_factor) + let (width, height): (u32, u32) = LogicalSize::new(*width, *height) + .to_physical::(scale_factor) .into(); - let position = PhysicalPosition::new( - (screen_size.width as f64 - width) / 2., - (screen_size.height as f64 - height) / 2., - ); + let position = PhysicalPosition { + x: screen_size.width.saturating_sub(width) as f64 / 2. + + monitor.position().x as f64, + y: screen_size.height.saturating_sub(height) as f64 / 2. + + monitor.position().y as f64, + }; winit_window_builder = winit_window_builder.with_position(position); + } else { + warn!("Couldn't get monitor selected with: {monitor_selection:?}"); } } At(position) => {