From f1ca3ccc695c77940a2e47bbe59406c4f6848c71 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 12 Nov 2020 22:06:57 -0500 Subject: [PATCH 1/2] Fix language and ui resolution in menus not updating when they are changed ingame --- voxygen/src/menu/char_selection/mod.rs | 9 ++++++++- voxygen/src/menu/char_selection/ui/mod.rs | 6 ++++++ voxygen/src/menu/main/mod.rs | 15 +++++++++++++-- voxygen/src/menu/main/ui/mod.rs | 7 +++++-- voxygen/src/window.rs | 9 ++++++++- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 7fc03783de..3a62263eb7 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -57,9 +57,16 @@ impl CharSelectionState { } impl PlayState for CharSelectionState { - fn enter(&mut self, _: &mut GlobalState, _: Direction) { + fn enter(&mut self, global_state: &mut GlobalState, _: Direction) { // Load the player's character list self.client.borrow_mut().load_character_list(); + + // Updated localization in case the selected language was changed + let localized_strings = crate::i18n::Localization::load_expect( + &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), + ); + self.char_selection_ui + .update_language(std::sync::Arc::clone(&localized_strings)); } fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 8fa52d72a0..5392a4489e 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -1410,6 +1410,12 @@ impl CharSelectionUi { } } + pub fn update_language(&mut self, i18n: std::sync::Arc) { + self.controls.i18n = i18n; + self.controls.fonts = Fonts::load(&self.controls.i18n.fonts, &mut self.ui) + .expect("Impossible to load fonts!"); + } + // TODO: do we need whole client here or just character list? pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec { let mut events = Vec::new(); diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index f3b57d1acc..e9362bf0cf 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -46,6 +46,15 @@ impl PlayState for MainMenuState { { global_state.singleplayer = None; } + + // Updated localization in case the selected language was changed + let localized_strings = crate::i18n::Localization::load_expect( + &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), + ); + self.main_menu_ui.update_language( + std::sync::Arc::clone(&localized_strings), + &global_state.settings, + ); } fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { @@ -258,8 +267,10 @@ impl PlayState for MainMenuState { &global_state.settings.language.selected_language, )); localized_strings.log_missing_entries(); - self.main_menu_ui - .update_language(std::sync::Arc::clone(&localized_strings)); + self.main_menu_ui.update_language( + std::sync::Arc::clone(&localized_strings), + &global_state.settings, + ); }, #[cfg(feature = "singleplayer")] MainMenuEvent::StartSingleplayer => { diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index 4515d61c4b..0955c897ea 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -363,7 +363,6 @@ impl Controls { }, Message::Username(new_value) => self.login_info.username = new_value, Message::LanguageChanged(new_value) => { - self.selected_language_index = Some(new_value); events.push(Event::ChangeLanguage(language_metadatas.remove(new_value))); }, Message::OpenLanguageMenu => self.is_selecting_language = !self.is_selecting_language, @@ -522,10 +521,14 @@ impl<'a> MainMenuUi { Self { ui, controls } } - pub fn update_language(&mut self, i18n: std::sync::Arc) { + pub fn update_language(&mut self, i18n: std::sync::Arc, settings: &Settings) { self.controls.i18n = i18n; self.controls.fonts = Fonts::load(&self.controls.i18n.fonts, &mut self.ui) .expect("Impossible to load fonts!"); + let language_metadatas = crate::i18n::list_localizations(); + self.controls.selected_language_index = language_metadatas + .iter() + .position(|f| f.language_identifier == settings.language.selected_language); } pub fn auth_trust_prompt(&mut self, auth_server: String) { diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index d6a33c2458..8a1b65351c 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -647,8 +647,15 @@ impl Window { pub fn fetch_events(&mut self) -> Vec { // Refresh ui size (used when changing playstates) if self.needs_refresh_resize { + let logical_size = self.logical_size(); self.events - .push(Event::Ui(ui::Event::new_resize(self.logical_size()))); + .push(Event::Ui(ui::Event::new_resize(logical_size))); + self.events.push(Event::IcedUi(iced::Event::Window( + iced::window::Event::Resized { + width: logical_size.x as u32, + height: logical_size.y as u32, + }, + ))); self.needs_refresh_resize = false; } From 5fa2ac6e38d6d26caef3628445a6d0c4376d328f Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 12 Nov 2020 23:39:47 -0500 Subject: [PATCH 2/2] 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, .. } => {