diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 2d75e9abb6adf..22fd64a960b74 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -78,7 +78,7 @@ pub fn camera_system( for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() { if let Some(window) = windows.get(camera.window) { if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) { - camera_projection.update(window.logical_width(), window.logical_height()); + camera_projection.update(window.width(), window.height()); camera.projection_matrix = camera_projection.get_projection_matrix(); camera.depth_calculation = camera_projection.depth_calculation(); } diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 0a663b3b6c204..f8851626d6cf7 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -116,8 +116,8 @@ impl FlexSurface { *node, stretch::style::Style { size: stretch::geometry::Size { - width: stretch::style::Dimension::Points(window.logical_width()), - height: stretch::style::Dimension::Points(window.logical_height()), + width: stretch::style::Dimension::Points(window.width()), + height: stretch::style::Dimension::Points(window.height()), }, ..Default::default() }, diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index c7996bdffebf9..ab173dd006e6f 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -32,11 +32,29 @@ impl Default for WindowId { } } +/// 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, + scale_factor: f64, title: String, vsync: bool, resizable: bool, @@ -48,7 +66,6 @@ pub struct Window { #[cfg(target_arch = "wasm32")] pub canvas: Option, command_queue: Vec, - scale_factor: f64, } #[derive(Debug)] @@ -61,8 +78,7 @@ pub enum WindowCommand { title: String, }, SetResolution { - physical_width: u32, - physical_height: u32, + resolution: (f32, f32), }, SetVsync { vsync: bool, @@ -100,11 +116,20 @@ pub enum WindowMode { } impl Window { - pub fn new(id: WindowId, window_descriptor: &WindowDescriptor) -> Self { + pub fn new( + id: WindowId, + window_descriptor: &WindowDescriptor, + physical_width: u32, + physical_height: u32, + scale_factor: f64, + ) -> Self { Window { id, - physical_height: window_descriptor.height, - physical_width: window_descriptor.width, + requested_width: window_descriptor.width, + requested_height: window_descriptor.height, + physical_width, + physical_height, + scale_factor, title: window_descriptor.title.clone(), vsync: window_descriptor.vsync, resizable: window_descriptor.resizable, @@ -116,7 +141,6 @@ impl Window { #[cfg(target_arch = "wasm32")] canvas: window_descriptor.canvas.clone(), command_queue: Vec::new(), - scale_factor: 1.0, } } @@ -125,31 +149,45 @@ impl Window { self.id } + /// The current logical width of the window's client area. #[inline] - pub fn width(&self) -> u32 { - self.logical_width() as u32 + 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] - pub fn height(&self) -> u32 { - self.logical_height() as u32 + 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. #[inline] - pub fn logical_width(&self) -> f32 { - (self.physical_width as f64 / self.scale_factor) as f32 + 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. #[inline] - pub fn logical_height(&self) -> f32 { - (self.physical_height as f64 / self.scale_factor) as f32 + pub fn requested_height(&self) -> f32 { + self.requested_height } + /// The window's client area width in physical pixels. #[inline] pub fn physical_width(&self) -> u32 { self.physical_width } + /// The window's client area height in physical pixels. #[inline] pub fn physical_height(&self) -> u32 { self.physical_height @@ -161,28 +199,32 @@ impl Window { .push(WindowCommand::SetMaximized { maximized }); } - pub fn set_resolution(&mut self, width: u32, height: u32) { - self.physical_width = (width as f64 * self.scale_factor) as u32; - self.physical_height = (height as f64 * self.scale_factor) as u32; + /// Request the OS to resize the window such the the client area matches the + /// specified width and height. + pub fn set_resolution(&mut self, width: f32, height: f32) { + self.requested_width = width; + self.requested_height = height; self.command_queue.push(WindowCommand::SetResolution { - physical_width: self.physical_width, - physical_height: self.physical_height, + resolution: (self.requested_width, self.requested_height), }); } #[allow(missing_docs)] #[inline] - pub fn update_physical_size_from_backend(&mut self, width: u32, height: u32) { - self.physical_width = width; - self.physical_height = height; + pub fn update_scale_factor_from_backend(&mut self, scale_factor: f64) { + self.scale_factor = scale_factor; } #[allow(missing_docs)] #[inline] - pub fn update_scale_factor_from_backend(&mut self, scale_factor: f64) { - self.scale_factor = scale_factor; + 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 ratio of physical pixels to logical pixels + /// + /// `physical_pixels = logical_pixels * scale_factor` #[inline] pub fn scale_factor(&self) -> f64 { self.scale_factor @@ -291,8 +333,8 @@ impl Window { #[derive(Debug, Clone)] pub struct WindowDescriptor { - pub width: u32, - pub height: u32, + pub width: f32, + pub height: f32, pub title: String, pub vsync: bool, pub resizable: bool, @@ -308,8 +350,8 @@ impl Default for WindowDescriptor { fn default() -> Self { WindowDescriptor { title: "bevy".to_string(), - width: 1280, - height: 720, + width: 1280., + height: 720., vsync: true, resizable: true, decorations: true, diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 1b4f850870850..6f80358aa6ef6 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -14,8 +14,8 @@ use bevy_ecs::{Resources, World}; use bevy_math::Vec2; use bevy_utils::tracing::{error, trace}; use bevy_window::{ - CreateWindow, CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, Window, - WindowCloseRequested, WindowCreated, WindowFocused, WindowResized, Windows, + CreateWindow, CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, WindowCloseRequested, + WindowCreated, WindowFocused, WindowResized, Windows, }; use winit::{ event::{self, DeviceEvent, Event, WindowEvent}, @@ -68,13 +68,12 @@ fn change_window(_: &mut World, resources: &mut Resources) { window.set_title(&title); } bevy_window::WindowCommand::SetResolution { - physical_width, - physical_height, + resolution: (logical_width, logical_height), } => { let window = winit_windows.get_window(id).unwrap(); - window.set_inner_size(winit::dpi::PhysicalSize::new( - physical_width, - physical_height, + window.set_inner_size(winit::dpi::LogicalSize::new( + logical_width, + logical_height, )); } bevy_window::WindowCommand::SetVsync { .. } => (), @@ -194,14 +193,13 @@ pub fn winit_runner(mut app: App) { let mut windows = app.resources.get_mut::().unwrap(); let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); let window = windows.get_mut(window_id).unwrap(); - window.update_physical_size_from_backend(size.width, size.height); - + window.update_actual_size_from_backend(size.width, size.height); let mut resize_events = app.resources.get_mut::>().unwrap(); resize_events.send(WindowResized { id: window_id, - height: window.logical_height(), - width: window.logical_width(), + width: window.width(), + height: window.height(), }); } WindowEvent::CloseRequested => { @@ -299,7 +297,7 @@ pub fn winit_runner(mut app: App) { // FIXME?: On Android window start is top while on PC/Linux/OSX on bottom if cfg!(target_os = "android") { - let window_height = windows.get_primary().unwrap().logical_height(); + let window_height = windows.get_primary().unwrap().height(); location.y = window_height - location.y; } touch_input_events.send(converters::convert_touch_input(touch, location)); @@ -326,11 +324,13 @@ pub fn winit_runner(mut app: App) { let mut windows = app.resources.get_mut::().unwrap(); let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); let window = windows.get_mut(window_id).unwrap(); - window.update_physical_size_from_backend( + window.update_actual_size_from_backend( new_inner_size.width, new_inner_size.height, ); window.update_scale_factor_from_backend(scale_factor); + // should we send a resize event to indicate the change in + // logical size? } WindowEvent::Focused(focused) => { let mut focused_events = @@ -382,10 +382,14 @@ fn handle_create_window_events( let create_window_events = resources.get::>().unwrap(); let mut window_created_events = resources.get_mut::>().unwrap(); for create_window_event in create_window_event_reader.iter(&create_window_events) { - let mut window = Window::new(create_window_event.id, &create_window_event.descriptor); - winit_windows.create_window(event_loop, &mut window); - let window_id = window.id(); + let window = winit_windows.create_window( + event_loop, + create_window_event.id, + &create_window_event.descriptor, + ); windows.add(window); - window_created_events.send(WindowCreated { id: window_id }); + window_created_events.send(WindowCreated { + id: create_window_event.id, + }); } } diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 75240017b0093..f2bee4c6d73f5 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -1,5 +1,5 @@ use bevy_utils::HashMap; -use bevy_window::{Window, WindowId, WindowMode}; +use bevy_window::{Window, WindowDescriptor, WindowId, WindowMode}; #[derive(Debug, Default)] pub struct WinitWindows { @@ -12,8 +12,9 @@ impl WinitWindows { pub fn create_window( &mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, - window: &mut Window, - ) { + window_id: WindowId, + window_descriptor: &WindowDescriptor, + ) -> Window { #[cfg(target_os = "windows")] let mut winit_window_builder = { use winit::platform::windows::WindowBuilderExtWindows; @@ -23,7 +24,7 @@ impl WinitWindows { #[cfg(not(target_os = "windows"))] let mut winit_window_builder = winit::window::WindowBuilder::new(); - winit_window_builder = match window.mode() { + winit_window_builder = match window_descriptor.mode { WindowMode::BorderlessFullscreen => winit_window_builder.with_fullscreen(Some( winit::window::Fullscreen::Borderless(event_loop.primary_monitor()), )), @@ -31,30 +32,30 @@ impl WinitWindows { winit::window::Fullscreen::Exclusive(match use_size { true => get_fitting_videomode( &event_loop.primary_monitor().unwrap(), - window.physical_width(), - window.physical_height(), + window_descriptor.width as u32, + window_descriptor.height as u32, ), false => get_best_videomode(&event_loop.primary_monitor().unwrap()), }), )), _ => winit_window_builder .with_inner_size(winit::dpi::LogicalSize::new( - window.width(), - window.height(), + window_descriptor.width, + window_descriptor.height, )) - .with_resizable(window.resizable()) - .with_decorations(window.decorations()), + .with_resizable(window_descriptor.resizable) + .with_decorations(window_descriptor.decorations), }; #[allow(unused_mut)] - let mut winit_window_builder = winit_window_builder.with_title(window.title()); + let mut winit_window_builder = winit_window_builder.with_title(&window_descriptor.title); #[cfg(target_arch = "wasm32")] { use wasm_bindgen::JsCast; use winit::platform::web::WindowBuilderExtWebSys; - if let Some(selector) = &window.canvas { + if let Some(selector) = &window_descriptor.canvas { let window = web_sys::window().unwrap(); let document = window.document().unwrap(); let canvas = document @@ -71,24 +72,22 @@ impl WinitWindows { let winit_window = winit_window_builder.build(&event_loop).unwrap(); - match winit_window.set_cursor_grab(window.cursor_locked()) { + match winit_window.set_cursor_grab(window_descriptor.cursor_locked) { Ok(_) => {} Err(winit::error::ExternalError::NotSupported(_)) => {} Err(err) => Err(err).unwrap(), } - winit_window.set_cursor_visible(window.cursor_visible()); + 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(window_id, winit_window.id()); + self.winit_to_window_id.insert(winit_window.id(), window_id); #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; - if window.canvas.is_none() { + if window_descriptor.canvas.is_none() { let canvas = winit_window.canvas(); let window = web_sys::window().unwrap(); @@ -101,10 +100,16 @@ impl WinitWindows { } let inner_size = winit_window.inner_size(); - window.update_physical_size_from_backend(inner_size.width, inner_size.height); - window.update_scale_factor_from_backend(winit_window.scale_factor()); - + let scale_factor = winit_window.scale_factor(); self.windows.insert(winit_window.id(), winit_window); + + Window::new( + window_id, + &window_descriptor, + inner_size.width, + inner_size.height, + scale_factor, + ) } pub fn get_window(&self, id: WindowId) -> Option<&winit::window::Window> { diff --git a/examples/2d/contributors.rs b/examples/2d/contributors.rs index af76c7ef9ad53..7fe61287ff365 100644 --- a/examples/2d/contributors.rs +++ b/examples/2d/contributors.rs @@ -238,11 +238,11 @@ fn collision_system( let win = wins.get_primary().unwrap(); - let ceiling = win.logical_height() / 2.; - let ground = -(win.logical_height() / 2.); + let ceiling = win.height() / 2.; + let ground = -(win.height() / 2.); - let wall_left = -(win.logical_width() / 2.); - let wall_right = win.logical_width() / 2.; + let wall_left = -(win.width() / 2.); + let wall_right = win.width() / 2.; for (mut v, mut t) in q.iter_mut() { let left = t.translation.x - SPRITE_SIZE / 2.0; diff --git a/examples/ecs/parallel_query.rs b/examples/ecs/parallel_query.rs index 3e36c16648fa2..cbb77af206bd4 100644 --- a/examples/ecs/parallel_query.rs +++ b/examples/ecs/parallel_query.rs @@ -48,8 +48,8 @@ fn bounce_system( mut sprites: Query<(&Transform, &mut Velocity)>, ) { let window = windows.get_primary().expect("No primary window."); - let width = window.logical_width(); - let height = window.logical_height(); + let width = window.width(); + let height = window.height(); let left = width / -2.0; let right = width / 2.0; let bottom = height / -2.0; diff --git a/examples/tools/bevymark.rs b/examples/tools/bevymark.rs index 475d7224b9be8..fde32c40cbacc 100644 --- a/examples/tools/bevymark.rs +++ b/examples/tools/bevymark.rs @@ -30,8 +30,8 @@ fn main() { App::build() .add_resource(WindowDescriptor { title: "BevyMark".to_string(), - width: 800, - height: 600, + width: 800., + height: 600., vsync: true, resizable: false, ..Default::default() @@ -85,8 +85,8 @@ fn mouse_handler( ) { if mouse_button_input.pressed(MouseButton::Left) { let spawn_count = (BIRDS_PER_SECOND as f32 * time.delta_seconds()) as u128; - let bird_x = (window.width as i32 / -2) as f32 + HALF_BIRD_SIZE; - let bird_y = (window.height / 2) as f32 - HALF_BIRD_SIZE; + let bird_x = (window.width / -2.) + HALF_BIRD_SIZE; + let bird_y = (window.height / 2.) - HALF_BIRD_SIZE; for count in 0..spawn_count { let bird_position = Vec3::new(bird_x, bird_y, (counter.count + count) as f32 * 0.00001); diff --git a/examples/wasm/winit_wasm.rs b/examples/wasm/winit_wasm.rs index c2c3ce8cd0086..14b48b1de2f29 100644 --- a/examples/wasm/winit_wasm.rs +++ b/examples/wasm/winit_wasm.rs @@ -9,8 +9,8 @@ use bevy::{ fn main() { App::build() .add_resource(WindowDescriptor { - width: 300, - height: 300, + width: 300., + height: 300., ..Default::default() }) .add_plugins(DefaultPlugins) diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index e86cbd55e832c..5eca81b4dc405 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -35,8 +35,8 @@ fn setup( create_window_events.send(CreateWindow { id: window_id, descriptor: WindowDescriptor { - width: 800, - height: 600, + width: 800., + height: 600., vsync: false, title: "second window".to_string(), ..Default::default() diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index 17fefd6f7b52f..0dc4fbd67ecfc 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -5,8 +5,8 @@ fn main() { App::build() .add_resource(WindowDescriptor { title: "I am a window!".to_string(), - width: 500, - height: 300, + width: 500., + height: 300., vsync: true, resizable: false, ..Default::default()