From 5fa2ac6e38d6d26caef3628445a6d0c4376d328f Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 12 Nov 2020 23:39:47 -0500 Subject: [PATCH] Handle window resizing properly to fix some bugs on startup --- voxygen/src/menu/char_selection/ui/mod.rs | 4 ++ voxygen/src/menu/main/mod.rs | 10 ++-- voxygen/src/menu/main/ui/mod.rs | 19 +++++++- voxygen/src/render/renderer.rs | 2 + voxygen/src/ui/ice/mod.rs | 59 ++++++++++++----------- voxygen/src/ui/ice/renderer/mod.rs | 30 +++++++----- voxygen/src/ui/mod.rs | 10 ++-- voxygen/src/ui/scale.rs | 32 ++++++++---- voxygen/src/window.rs | 7 ++- 9 files changed, 111 insertions(+), 62 deletions(-) diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 5392a4489e..8df8486b9d 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1406,6 +1406,10 @@ impl CharSelectionUi { window::Event::MouseButton(_, window::PressState::Pressed) => { !self.controls.mouse_detector.mouse_over() }, + window::Event::ScaleFactorChanged(s) => { + self.ui.scale_factor_changed(s); + false + }, _ => false, } } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index e9362bf0cf..54888112a5 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -57,6 +57,7 @@ impl PlayState for MainMenuState { ); } + #[allow(clippy::single_match)] // TODO: remove when event match has multiple arms fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { span!(_guard, "tick", "::tick"); let mut localized_strings = crate::i18n::Localization::load_expect( @@ -93,12 +94,13 @@ impl PlayState for MainMenuState { // Handle window events. for event in events { + // Pass all events to the ui first. + if self.main_menu_ui.handle_event(event.clone()) { + continue; + } + match event { Event::Close => return PlayStateResult::Shutdown, - // Pass events to ui. - Event::IcedUi(event) => { - self.main_menu_ui.handle_event(event); - }, // Ignore all other events. _ => {}, } diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index 0955c897ea..686eedbc75 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -14,7 +14,7 @@ use crate::{ img_ids::{ImageGraphic, VoxelGraphic}, Graphic, }, - GlobalState, + window, GlobalState, }; use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space}; //ImageFrame, Tooltip, @@ -541,7 +541,22 @@ impl<'a> MainMenuUi { pub fn cancel_connection(&mut self) { self.controls.exit_connect_screen(); } - pub fn handle_event(&mut self, event: ui::ice::Event) { + pub fn handle_event(&mut self, event: window::Event) -> bool { + match event { + // Pass events to ui. + window::Event::IcedUi(event) => { + self.handle_ui_event(event); + true + }, + window::Event::ScaleFactorChanged(s) => { + self.ui.scale_factor_changed(s); + false + }, + _ => false, + } + } + + pub fn handle_ui_event(&mut self, event: ui::ice::Event) { // Tab for input fields use iced::keyboard; if matches!( diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 26025e5d7a..fe6a8f8b8a 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -601,6 +601,8 @@ impl Renderer { } /// Get the resolution of the render target. + /// Note: the change after a resize can be delayed so + /// don't rely on this value being constant between resize events pub fn get_resolution(&self) -> Vec2 { Vec2::new( self.win_color_view.get_dimensions().0, diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index caa25ea51f..cae5a18a24 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -30,8 +30,7 @@ pub struct IcedUi { cursor_position: Vec2, // Scaling of the ui scale: Scale, - window_resized: Option>, - scale_mode_changed: bool, + scale_changed: bool, } impl IcedUi { pub fn new( @@ -42,19 +41,24 @@ impl IcedUi { let scale = Scale::new(window, scale_mode, 1.2); let renderer = window.renderer_mut(); - let scaled_dims = scale.scaled_window_size().map(|e| e as f32); + let scaled_resolution = scale.scaled_resolution().map(|e| e as f32); + let physical_resolution = scale.physical_resolution(); // TODO: examine how much mem fonts take up and reduce clones if significant Ok(Self { - renderer: IcedRenderer::new(renderer, scaled_dims, default_font)?, + renderer: IcedRenderer::new( + renderer, + scaled_resolution, + physical_resolution, + default_font, + )?, cache: Some(Cache::new()), events: Vec::new(), // TODO: handle None clipboard: Clipboard::new(window.window()).unwrap(), cursor_position: Vec2::zero(), scale, - window_resized: None, - scale_mode_changed: false, + scale_changed: false, }) } @@ -75,7 +79,13 @@ impl IcedUi { pub fn set_scaling_mode(&mut self, mode: ScaleMode) { self.scale.set_scaling_mode(mode); // Signal that change needs to be handled - self.scale_mode_changed = true; + self.scale_changed = true; + } + + /// Dpi factor changed + /// Not to be confused with scaling mode + pub fn scale_factor_changed(&mut self, scale_factor: f64) { + self.scale_changed |= self.scale.scale_factor_changed(scale_factor); } pub fn handle_event(&mut self, event: Event) { @@ -86,7 +96,10 @@ impl IcedUi { // ideally these values should be the logical ones Event::Window(window::Event::Resized { width, height }) => { if width != 0 && height != 0 { - self.window_resized = Some(Vec2::new(width, height)); + let new_dims = Vec2::new(width, height); + // TODO maybe use u32 in Scale to be consistent with iced + // Avoid resetting cache if window size didn't change + self.scale_changed |= self.scale.window_resized(new_dims.map(|e| e as f64)); } }, // Scale cursor movement events @@ -133,32 +146,22 @@ impl IcedUi { renderer: &mut Renderer, ) -> (Vec, mouse::Interaction) { span!(_guard, "maintain", "IcedUi::maintain"); - // Handle window resizing and scale mode changing - let scaled_dims = if let Some(new_dims) = self.window_resized.take() { - // TODO maybe use u32 in Scale to be consistent with iced - self.scale - .window_resized(new_dims.map(|e| e as f64), renderer) - // Avoid resetting cache if window size didn't change - .then(|| self.scale.scaled_window_size()) - } else if self.scale_mode_changed { - Some(self.scale.scaled_window_size()) - } else { - None - }; - if let Some(scaled_dims) = scaled_dims { - self.scale_mode_changed = false; + // Handle window resizing, dpi factor change, and scale mode changing + if self.scale_changed { + self.scale_changed = false; + let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32); self.events .push(Event::Window(iced::window::Event::Resized { - width: scaled_dims.x as u32, - height: scaled_dims.y as u32, + width: scaled_resolution.x as u32, + height: scaled_resolution.y as u32, })); // Avoid panic in graphic cache when minimizing. // Somewhat inefficient for elements that won't change size after a window // resize - let res = renderer.get_resolution(); - if res.x > 0 && res.y > 0 { + let physical_resolution = self.scale.physical_resolution(); + if physical_resolution.map(|e| e > 0).reduce_and() { self.renderer - .resize(scaled_dims.map(|e| e as f32), renderer); + .resize(scaled_resolution, physical_resolution, renderer); } } @@ -168,7 +171,7 @@ impl IcedUi { }; // TODO: convert to f32 at source - let window_size = self.scale.scaled_window_size().map(|e| e as f32); + let window_size = self.scale.scaled_resolution().map(|e| e as f32); span!(guard, "build user_interface"); let mut user_interface = UserInterface::build( diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 03721eff98..c697d36f81 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -117,11 +117,12 @@ pub struct IcedRenderer { impl IcedRenderer { pub fn new( renderer: &mut Renderer, - scaled_dims: Vec2, + scaled_resolution: Vec2, + physical_resolution: Vec2, default_font: Font, ) -> Result { let (half_res, align, p_scale) = - Self::calculate_resolution_dependents(renderer.get_resolution(), scaled_dims); + Self::calculate_resolution_dependents(physical_resolution, scaled_resolution); Ok(Self { cache: Cache::new(renderer, default_font)?, @@ -137,8 +138,8 @@ impl IcedRenderer { half_res, align, p_scale, - win_dims: scaled_dims, - window_scissor: default_scissor(renderer), + win_dims: scaled_resolution, + window_scissor: default_scissor(physical_resolution), start: 0, }) } @@ -162,11 +163,16 @@ impl IcedRenderer { .unwrap() } - pub fn resize(&mut self, scaled_dims: Vec2, renderer: &mut Renderer) { - self.win_dims = scaled_dims; - self.window_scissor = default_scissor(renderer); + pub fn resize( + &mut self, + scaled_resolution: Vec2, + physical_resolution: Vec2, + renderer: &mut Renderer, + ) { + self.win_dims = scaled_resolution; + self.window_scissor = default_scissor(physical_resolution); - self.update_resolution_dependents(renderer.get_resolution()); + self.update_resolution_dependents(physical_resolution); // Resize graphic cache self.cache.resize_graphic_cache(renderer); @@ -194,7 +200,7 @@ impl IcedRenderer { // Draw glyph cache (use for debugging). /*self.draw_commands - .push(DrawCommand::Scissor(default_scissor(renderer))); + .push(DrawCommand::Scissor(self.window_scissor)); self.start = self.mesh.vertices().len(); self.mesh.push_quad(create_ui_quad( Aabr { @@ -758,7 +764,7 @@ impl IcedRenderer { pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts>) { span!(_guard, "render", "IcedRenderer::render"); - let mut scissor = default_scissor(renderer); + let mut scissor = self.window_scissor; let globals = maybe_globals.unwrap_or(&self.default_globals); let mut locals = &self.interface_locals; for draw_command in self.draw_commands.iter() { @@ -793,8 +799,8 @@ fn align(res: Vec2) -> Vec2 { res.map(|e| (e & 1) as f32 * 0.5) } -fn default_scissor(renderer: &Renderer) -> Aabr { - let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as u16).into_tuple(); +fn default_scissor(physical_resolution: Vec2) -> Aabr { + let (screen_w, screen_h) = physical_resolution.into_tuple(); Aabr { min: Vec2 { x: 0, y: 0 }, max: Vec2 { diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index e7fc961c17..363ae5442b 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -126,7 +126,7 @@ pub struct Ui { impl Ui { pub fn new(window: &mut Window) -> Result { let scale = Scale::new(window, ScaleMode::Absolute(1.0), 1.0); - let win_dims = scale.scaled_window_size().into_array(); + let win_dims = scale.scaled_resolution().into_array(); let renderer = window.renderer_mut(); @@ -165,7 +165,7 @@ impl Ui { // To clear the cache (it won't be resized in this case) self.need_cache_resize = true; // Give conrod the new size. - let (w, h) = self.scale.scaled_window_size().into_tuple(); + let (w, h) = self.scale.scaled_resolution().into_tuple(); self.ui.handle_event(Input::Resize(w, h)); } @@ -286,9 +286,9 @@ impl Ui { // Handle window resizing. if let Some(new_dims) = self.window_resized.take() { - let (old_w, old_h) = self.scale.scaled_window_size().into_tuple(); - self.scale.window_resized(new_dims, renderer); - let (w, h) = self.scale.scaled_window_size().into_tuple(); + let (old_w, old_h) = self.scale.scaled_resolution().into_tuple(); + self.scale.window_resized(new_dims); + let (w, h) = self.scale.scaled_resolution().into_tuple(); self.ui.handle_event(Input::Resize(w, h)); // Avoid panic in graphic cache when minimizing. diff --git a/voxygen/src/ui/scale.rs b/voxygen/src/ui/scale.rs index 34bcd5b171..669adcd240 100644 --- a/voxygen/src/ui/scale.rs +++ b/voxygen/src/ui/scale.rs @@ -1,4 +1,4 @@ -use crate::{render::Renderer, window::Window}; +use crate::window::Window; use serde::{Deserialize, Serialize}; use vek::*; @@ -31,7 +31,7 @@ pub struct Scale { impl Scale { pub fn new(window: &Window, mode: ScaleMode, extra_factor: f64) -> Self { let window_dims = window.logical_size(); - let scale_factor = window.renderer().get_resolution().x as f64 / window_dims.x; + let scale_factor = window.scale_factor(); Scale { mode, scale_factor, @@ -74,22 +74,34 @@ impl Scale { /// Multiply by scaled coordinates to get the physical coordinates pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor } - /// Updates internal window size (and/or scale_factor). - /// Returns true if either value was changed + /// Updates window size + /// Returns true if the value was changed #[allow(clippy::float_cmp)] - pub fn window_resized(&mut self, new_dims: Vec2, renderer: &Renderer) -> bool { - let old_scale_factor = self.scale_factor; + pub fn window_resized(&mut self, new_dims: Vec2) -> bool { let old_window_dims = self.window_dims; - self.scale_factor = renderer.get_resolution().x as f64 / new_dims.x; self.window_dims = new_dims; - old_scale_factor != self.scale_factor || old_window_dims != self.window_dims + old_window_dims != self.window_dims + } + + /// Updates scale factor + /// Returns true if the value was changed + #[allow(clippy::float_cmp)] + pub fn scale_factor_changed(&mut self, scale_factor: f64) -> bool { + let old_scale_factor = self.scale_factor; + self.scale_factor = scale_factor; + old_scale_factor != self.scale_factor } /// Get scaled window size. - pub fn scaled_window_size(&self) -> Vec2 { self.window_dims / self.scale_factor_logical() } + pub fn scaled_resolution(&self) -> Vec2 { self.window_dims / self.scale_factor_logical() } /// Get logical window size - pub fn window_size(&self) -> Vec2 { self.window_dims } + pub fn logical_resolution(&self) -> Vec2 { self.window_dims } + + /// Get physical window size + pub fn physical_resolution(&self) -> Vec2 { + (self.window_dims * self.scale_factor).map(|e| e.round() as u16) + } // Transform point from logical to scaled coordinates. pub fn scale_point(&self, point: Vec2) -> Vec2 { point / self.scale_factor_logical() } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 8a1b65351c..d271ca5b95 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -266,6 +266,8 @@ pub enum Event { Close, /// The window has been resized. Resize(Vec2), + /// The window scale factor has been changed + ScaleFactorChanged(f64), /// The window has been moved. Moved(Vec2), /// A key has been typed that corresponds to a specific character. @@ -656,6 +658,8 @@ impl Window { height: logical_size.y as u32, }, ))); + self.events + .push(Event::ScaleFactorChanged(self.scale_factor)); self.needs_refresh_resize = false; } @@ -934,7 +938,8 @@ impl Window { .push(Event::Resize(Vec2::new(width as u32, height as u32))); }, WindowEvent::ScaleFactorChanged { scale_factor, .. } => { - self.scale_factor = scale_factor + self.scale_factor = scale_factor; + self.events.push(Event::ScaleFactorChanged(scale_factor)); }, WindowEvent::ReceivedCharacter(c) => self.events.push(Event::Char(c)), WindowEvent::MouseInput { button, state, .. } => {