Skip to content

Commit 309432e

Browse files
LoipesMasItsDoot
authored andcommitted
Add option to center a window (bevyengine#4999)
# Objective - Fixes bevyengine#4993 ## Solution - ~~Add `centered` property to `WindowDescriptor`~~ - Add `WindowPosition` enum - `WindowDescriptor.position` is now `WindowPosition` instead of `Option<Vec2>` - Add `center_window` function to `Window` ## Migration Guide - If using `WindowDescriptor`, replace `position: None` with `position: WindowPosition::Default` and `position: Some(vec2)` with `WindowPosition::At(vec2)`. I'm not sure if this is the best approach, so feel free to give any feedback. Also I'm not sure how `Option`s should be handled in `bevy_winit/src/lib.rs:161`. Also, on window creation we can't (or at least I couldn't) get `outer_size`, so this doesn't include decorations in calculations.
1 parent 4a3c3be commit 309432e

File tree

4 files changed

+122
-29
lines changed

4 files changed

+122
-29
lines changed

crates/bevy_window/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ pub use windows::*;
1616
pub mod prelude {
1717
#[doc(hidden)]
1818
pub use crate::{
19-
CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter,
20-
Window, WindowDescriptor, WindowMoved, Windows,
19+
CursorEntered, CursorIcon, CursorLeft, CursorMoved, FileDragAndDrop, MonitorSelection,
20+
ReceivedCharacter, Window, WindowDescriptor, WindowMoved, WindowPosition, Windows,
2121
};
2222
}
2323

crates/bevy_window/src/window.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ pub enum WindowCommand {
259259
SetPosition {
260260
position: IVec2,
261261
},
262+
/// Modifies the position of the window to be in the center of the current monitor
263+
Center(MonitorSelection),
262264
/// Set the window's [`WindowResizeConstraints`]
263265
SetResizeConstraints {
264266
resize_constraints: WindowResizeConstraints,
@@ -416,6 +418,17 @@ impl Window {
416418
.push(WindowCommand::SetPosition { position });
417419
}
418420

421+
/// Modifies the position of the window to be in the center of the current monitor
422+
///
423+
/// # Platform-specific
424+
/// - iOS: Can only be called on the main thread.
425+
/// - Web / Android / Wayland: Unsupported.
426+
#[inline]
427+
pub fn center_window(&mut self, monitor_selection: MonitorSelection) {
428+
self.command_queue
429+
.push(WindowCommand::Center(monitor_selection));
430+
}
431+
419432
/// Modifies the minimum and maximum window bounds for resizing in logical pixels.
420433
#[inline]
421434
pub fn set_resize_constraints(&mut self, resize_constraints: WindowResizeConstraints) {
@@ -714,6 +727,32 @@ impl Window {
714727
}
715728
}
716729

730+
/// Defines where window should be placed at on creation.
731+
#[derive(Debug, Clone, Copy)]
732+
pub enum WindowPosition {
733+
/// Position will be set by the window manager
734+
Automatic,
735+
/// Window will be centered on the selected monitor
736+
///
737+
/// Note that this does not account for window decorations.
738+
Centered(MonitorSelection),
739+
/// The window's top-left corner will be placed at the specified position (in pixels)
740+
///
741+
/// (0,0) represents top-left corner of screen space.
742+
At(Vec2),
743+
}
744+
745+
/// Defines which monitor to use.
746+
#[derive(Debug, Clone, Copy)]
747+
pub enum MonitorSelection {
748+
/// Uses current monitor of the window.
749+
Current,
750+
/// Uses primary monitor of the system.
751+
Primary,
752+
/// Uses monitor with the specified index.
753+
Number(usize),
754+
}
755+
717756
/// Describes the information needed for creating a window.
718757
///
719758
/// This should be set up before adding the [`WindowPlugin`](crate::WindowPlugin).
@@ -732,10 +771,8 @@ pub struct WindowDescriptor {
732771
///
733772
/// May vary from the physical height due to different pixel density on different monitors.
734773
pub height: f32,
735-
/// The position on the screen that the window will be centered at.
736-
///
737-
/// If set to `None`, some platform-specific position will be chosen.
738-
pub position: Option<Vec2>,
774+
/// The position on the screen that the window will be placed at.
775+
pub position: WindowPosition,
739776
/// Sets minimum and maximum resize limits.
740777
pub resize_constraints: WindowResizeConstraints,
741778
/// Overrides the window's ratio of physical pixels to logical pixels.
@@ -799,7 +836,7 @@ impl Default for WindowDescriptor {
799836
title: "app".to_string(),
800837
width: 1280.,
801838
height: 720.,
802-
position: None,
839+
position: WindowPosition::Automatic,
803840
resize_constraints: WindowResizeConstraints::default(),
804841
scale_factor_override: None,
805842
present_mode: PresentMode::Fifo,

crates/bevy_winit/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,31 @@ fn change_window(
162162
y: position[1],
163163
});
164164
}
165+
bevy_window::WindowCommand::Center(monitor_selection) => {
166+
let window = winit_windows.get_window(id).unwrap();
167+
168+
use bevy_window::MonitorSelection::*;
169+
let maybe_monitor = match monitor_selection {
170+
Current => window.current_monitor(),
171+
Primary => window.primary_monitor(),
172+
Number(n) => window.available_monitors().nth(n),
173+
};
174+
175+
if let Some(monitor) = maybe_monitor {
176+
let screen_size = monitor.size();
177+
178+
let window_size = window.outer_size();
179+
180+
window.set_outer_position(PhysicalPosition {
181+
x: screen_size.width.saturating_sub(window_size.width) as f64 / 2.
182+
+ monitor.position().x as f64,
183+
y: screen_size.height.saturating_sub(window_size.height) as f64 / 2.
184+
+ monitor.position().y as f64,
185+
});
186+
} else {
187+
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
188+
}
189+
}
165190
bevy_window::WindowCommand::SetResizeConstraints { resize_constraints } => {
166191
let window = winit_windows.get_window(id).unwrap();
167192
let constraints = resize_constraints.check_constraints();

crates/bevy_winit/src/winit_windows.rs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use bevy_math::IVec2;
2-
use bevy_utils::HashMap;
2+
use bevy_utils::{tracing::warn, HashMap};
33
use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode};
44
use raw_window_handle::HasRawWindowHandle;
5-
use winit::dpi::LogicalSize;
5+
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
66

77
#[derive(Debug, Default)]
88
pub struct WinitWindows {
@@ -49,30 +49,61 @@ impl WinitWindows {
4949
..
5050
} = window_descriptor;
5151

52-
if let Some(position) = position {
53-
if let Some(sf) = scale_factor_override {
54-
winit_window_builder = winit_window_builder.with_position(
55-
winit::dpi::LogicalPosition::new(
56-
position[0] as f64,
57-
position[1] as f64,
58-
)
59-
.to_physical::<f64>(*sf),
60-
);
61-
} else {
62-
winit_window_builder =
63-
winit_window_builder.with_position(winit::dpi::LogicalPosition::new(
64-
position[0] as f64,
65-
position[1] as f64,
66-
));
52+
use bevy_window::WindowPosition::*;
53+
match position {
54+
Automatic => { /* Window manager will handle position */ }
55+
Centered(monitor_selection) => {
56+
use bevy_window::MonitorSelection::*;
57+
let maybe_monitor = match monitor_selection {
58+
Current => {
59+
warn!("Can't select current monitor on window creation!");
60+
None
61+
}
62+
Primary => event_loop.primary_monitor(),
63+
Number(n) => event_loop.available_monitors().nth(*n),
64+
};
65+
66+
if let Some(monitor) = maybe_monitor {
67+
let screen_size = monitor.size();
68+
69+
let scale_factor = scale_factor_override.unwrap_or(1.0);
70+
71+
// Logical to physical window size
72+
let (width, height): (u32, u32) = LogicalSize::new(*width, *height)
73+
.to_physical::<u32>(scale_factor)
74+
.into();
75+
76+
let position = PhysicalPosition {
77+
x: screen_size.width.saturating_sub(width) as f64 / 2.
78+
+ monitor.position().x as f64,
79+
y: screen_size.height.saturating_sub(height) as f64 / 2.
80+
+ monitor.position().y as f64,
81+
};
82+
83+
winit_window_builder = winit_window_builder.with_position(position);
84+
} else {
85+
warn!("Couldn't get monitor selected with: {monitor_selection:?}");
86+
}
87+
}
88+
At(position) => {
89+
if let Some(sf) = scale_factor_override {
90+
winit_window_builder = winit_window_builder.with_position(
91+
LogicalPosition::new(position[0] as f64, position[1] as f64)
92+
.to_physical::<f64>(*sf),
93+
);
94+
} else {
95+
winit_window_builder = winit_window_builder.with_position(
96+
LogicalPosition::new(position[0] as f64, position[1] as f64),
97+
);
98+
}
6799
}
68100
}
101+
69102
if let Some(sf) = scale_factor_override {
70-
winit_window_builder.with_inner_size(
71-
winit::dpi::LogicalSize::new(*width, *height).to_physical::<f64>(*sf),
72-
)
73-
} else {
74103
winit_window_builder
75-
.with_inner_size(winit::dpi::LogicalSize::new(*width, *height))
104+
.with_inner_size(LogicalSize::new(*width, *height).to_physical::<f64>(*sf))
105+
} else {
106+
winit_window_builder.with_inner_size(LogicalSize::new(*width, *height))
76107
}
77108
}
78109
.with_resizable(window_descriptor.resizable)

0 commit comments

Comments
 (0)