Handle window resizing properly to fix some bugs on startup

This commit is contained in:
Imbris 2020-11-12 23:39:47 -05:00
parent f1ca3ccc69
commit 5fa2ac6e38
9 changed files with 111 additions and 62 deletions

View File

@ -1406,6 +1406,10 @@ impl CharSelectionUi {
window::Event::MouseButton(_, window::PressState::Pressed) => { window::Event::MouseButton(_, window::PressState::Pressed) => {
!self.controls.mouse_detector.mouse_over() !self.controls.mouse_detector.mouse_over()
}, },
window::Event::ScaleFactorChanged(s) => {
self.ui.scale_factor_changed(s);
false
},
_ => false, _ => false,
} }
} }

View File

@ -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<Event>) -> PlayStateResult { fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
span!(_guard, "tick", "<MainMenuState as PlayState>::tick"); span!(_guard, "tick", "<MainMenuState as PlayState>::tick");
let mut localized_strings = crate::i18n::Localization::load_expect( let mut localized_strings = crate::i18n::Localization::load_expect(
@ -93,12 +94,13 @@ impl PlayState for MainMenuState {
// Handle window events. // Handle window events.
for event in events { for event in events {
// Pass all events to the ui first.
if self.main_menu_ui.handle_event(event.clone()) {
continue;
}
match event { match event {
Event::Close => return PlayStateResult::Shutdown, Event::Close => return PlayStateResult::Shutdown,
// Pass events to ui.
Event::IcedUi(event) => {
self.main_menu_ui.handle_event(event);
},
// Ignore all other events. // Ignore all other events.
_ => {}, _ => {},
} }

View File

@ -14,7 +14,7 @@ use crate::{
img_ids::{ImageGraphic, VoxelGraphic}, img_ids::{ImageGraphic, VoxelGraphic},
Graphic, Graphic,
}, },
GlobalState, window, GlobalState,
}; };
use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space}; use iced::{text_input, Column, Container, HorizontalAlignment, Length, Row, Space};
//ImageFrame, Tooltip, //ImageFrame, Tooltip,
@ -541,7 +541,22 @@ impl<'a> MainMenuUi {
pub fn cancel_connection(&mut self) { self.controls.exit_connect_screen(); } 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 // Tab for input fields
use iced::keyboard; use iced::keyboard;
if matches!( if matches!(

View File

@ -601,6 +601,8 @@ impl Renderer {
} }
/// Get the resolution of the render target. /// 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<u16> { pub fn get_resolution(&self) -> Vec2<u16> {
Vec2::new( Vec2::new(
self.win_color_view.get_dimensions().0, self.win_color_view.get_dimensions().0,

View File

@ -30,8 +30,7 @@ pub struct IcedUi {
cursor_position: Vec2<f32>, cursor_position: Vec2<f32>,
// Scaling of the ui // Scaling of the ui
scale: Scale, scale: Scale,
window_resized: Option<Vec2<u32>>, scale_changed: bool,
scale_mode_changed: bool,
} }
impl IcedUi { impl IcedUi {
pub fn new( pub fn new(
@ -42,19 +41,24 @@ impl IcedUi {
let scale = Scale::new(window, scale_mode, 1.2); let scale = Scale::new(window, scale_mode, 1.2);
let renderer = window.renderer_mut(); 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 // TODO: examine how much mem fonts take up and reduce clones if significant
Ok(Self { Ok(Self {
renderer: IcedRenderer::new(renderer, scaled_dims, default_font)?, renderer: IcedRenderer::new(
renderer,
scaled_resolution,
physical_resolution,
default_font,
)?,
cache: Some(Cache::new()), cache: Some(Cache::new()),
events: Vec::new(), events: Vec::new(),
// TODO: handle None // TODO: handle None
clipboard: Clipboard::new(window.window()).unwrap(), clipboard: Clipboard::new(window.window()).unwrap(),
cursor_position: Vec2::zero(), cursor_position: Vec2::zero(),
scale, scale,
window_resized: None, scale_changed: false,
scale_mode_changed: false,
}) })
} }
@ -75,7 +79,13 @@ impl IcedUi {
pub fn set_scaling_mode(&mut self, mode: ScaleMode) { pub fn set_scaling_mode(&mut self, mode: ScaleMode) {
self.scale.set_scaling_mode(mode); self.scale.set_scaling_mode(mode);
// Signal that change needs to be handled // 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) { pub fn handle_event(&mut self, event: Event) {
@ -86,7 +96,10 @@ impl IcedUi {
// ideally these values should be the logical ones // ideally these values should be the logical ones
Event::Window(window::Event::Resized { width, height }) => { Event::Window(window::Event::Resized { width, height }) => {
if width != 0 && height != 0 { 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 // Scale cursor movement events
@ -133,32 +146,22 @@ impl IcedUi {
renderer: &mut Renderer, renderer: &mut Renderer,
) -> (Vec<M>, mouse::Interaction) { ) -> (Vec<M>, mouse::Interaction) {
span!(_guard, "maintain", "IcedUi::maintain"); span!(_guard, "maintain", "IcedUi::maintain");
// Handle window resizing and scale mode changing // Handle window resizing, dpi factor change, and scale mode changing
let scaled_dims = if let Some(new_dims) = self.window_resized.take() { if self.scale_changed {
// TODO maybe use u32 in Scale to be consistent with iced self.scale_changed = false;
self.scale let scaled_resolution = self.scale.scaled_resolution().map(|e| e as f32);
.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;
self.events self.events
.push(Event::Window(iced::window::Event::Resized { .push(Event::Window(iced::window::Event::Resized {
width: scaled_dims.x as u32, width: scaled_resolution.x as u32,
height: scaled_dims.y as u32, height: scaled_resolution.y as u32,
})); }));
// Avoid panic in graphic cache when minimizing. // Avoid panic in graphic cache when minimizing.
// Somewhat inefficient for elements that won't change size after a window // Somewhat inefficient for elements that won't change size after a window
// resize // resize
let res = renderer.get_resolution(); let physical_resolution = self.scale.physical_resolution();
if res.x > 0 && res.y > 0 { if physical_resolution.map(|e| e > 0).reduce_and() {
self.renderer 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 // 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"); span!(guard, "build user_interface");
let mut user_interface = UserInterface::build( let mut user_interface = UserInterface::build(

View File

@ -117,11 +117,12 @@ pub struct IcedRenderer {
impl IcedRenderer { impl IcedRenderer {
pub fn new( pub fn new(
renderer: &mut Renderer, renderer: &mut Renderer,
scaled_dims: Vec2<f32>, scaled_resolution: Vec2<f32>,
physical_resolution: Vec2<u16>,
default_font: Font, default_font: Font,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let (half_res, align, p_scale) = 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 { Ok(Self {
cache: Cache::new(renderer, default_font)?, cache: Cache::new(renderer, default_font)?,
@ -137,8 +138,8 @@ impl IcedRenderer {
half_res, half_res,
align, align,
p_scale, p_scale,
win_dims: scaled_dims, win_dims: scaled_resolution,
window_scissor: default_scissor(renderer), window_scissor: default_scissor(physical_resolution),
start: 0, start: 0,
}) })
} }
@ -162,11 +163,16 @@ impl IcedRenderer {
.unwrap() .unwrap()
} }
pub fn resize(&mut self, scaled_dims: Vec2<f32>, renderer: &mut Renderer) { pub fn resize(
self.win_dims = scaled_dims; &mut self,
self.window_scissor = default_scissor(renderer); scaled_resolution: Vec2<f32>,
physical_resolution: Vec2<u16>,
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 // Resize graphic cache
self.cache.resize_graphic_cache(renderer); self.cache.resize_graphic_cache(renderer);
@ -194,7 +200,7 @@ impl IcedRenderer {
// Draw glyph cache (use for debugging). // Draw glyph cache (use for debugging).
/*self.draw_commands /*self.draw_commands
.push(DrawCommand::Scissor(default_scissor(renderer))); .push(DrawCommand::Scissor(self.window_scissor));
self.start = self.mesh.vertices().len(); self.start = self.mesh.vertices().len();
self.mesh.push_quad(create_ui_quad( self.mesh.push_quad(create_ui_quad(
Aabr { Aabr {
@ -758,7 +764,7 @@ impl IcedRenderer {
pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts<Globals>>) { pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts<Globals>>) {
span!(_guard, "render", "IcedRenderer::render"); 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 globals = maybe_globals.unwrap_or(&self.default_globals);
let mut locals = &self.interface_locals; let mut locals = &self.interface_locals;
for draw_command in self.draw_commands.iter() { for draw_command in self.draw_commands.iter() {
@ -793,8 +799,8 @@ fn align(res: Vec2<u16>) -> Vec2<f32> {
res.map(|e| (e & 1) as f32 * 0.5) res.map(|e| (e & 1) as f32 * 0.5)
} }
fn default_scissor(renderer: &Renderer) -> Aabr<u16> { fn default_scissor(physical_resolution: Vec2<u16>) -> Aabr<u16> {
let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as u16).into_tuple(); let (screen_w, screen_h) = physical_resolution.into_tuple();
Aabr { Aabr {
min: Vec2 { x: 0, y: 0 }, min: Vec2 { x: 0, y: 0 },
max: Vec2 { max: Vec2 {

View File

@ -126,7 +126,7 @@ pub struct Ui {
impl Ui { impl Ui {
pub fn new(window: &mut Window) -> Result<Self, Error> { pub fn new(window: &mut Window) -> Result<Self, Error> {
let scale = Scale::new(window, ScaleMode::Absolute(1.0), 1.0); 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(); let renderer = window.renderer_mut();
@ -165,7 +165,7 @@ impl Ui {
// To clear the cache (it won't be resized in this case) // To clear the cache (it won't be resized in this case)
self.need_cache_resize = true; self.need_cache_resize = true;
// Give conrod the new size. // 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)); self.ui.handle_event(Input::Resize(w, h));
} }
@ -286,9 +286,9 @@ impl Ui {
// Handle window resizing. // Handle window resizing.
if let Some(new_dims) = self.window_resized.take() { if let Some(new_dims) = self.window_resized.take() {
let (old_w, old_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, renderer); self.scale.window_resized(new_dims);
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)); self.ui.handle_event(Input::Resize(w, h));
// Avoid panic in graphic cache when minimizing. // Avoid panic in graphic cache when minimizing.

View File

@ -1,4 +1,4 @@
use crate::{render::Renderer, window::Window}; use crate::window::Window;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use vek::*; use vek::*;
@ -31,7 +31,7 @@ pub struct Scale {
impl Scale { impl Scale {
pub fn new(window: &Window, mode: ScaleMode, extra_factor: f64) -> Self { pub fn new(window: &Window, mode: ScaleMode, extra_factor: f64) -> Self {
let window_dims = window.logical_size(); 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 { Scale {
mode, mode,
scale_factor, scale_factor,
@ -74,22 +74,34 @@ impl Scale {
/// Multiply by scaled coordinates to get the physical coordinates /// Multiply by scaled coordinates to get the physical coordinates
pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor } pub fn scale_factor_physical(&self) -> f64 { self.scale_factor_logical() * self.scale_factor }
/// Updates internal window size (and/or scale_factor). /// Updates window size
/// Returns true if either value was changed /// Returns true if the value was changed
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
pub fn window_resized(&mut self, new_dims: Vec2<f64>, renderer: &Renderer) -> bool { pub fn window_resized(&mut self, new_dims: Vec2<f64>) -> bool {
let old_scale_factor = self.scale_factor;
let old_window_dims = self.window_dims; let old_window_dims = self.window_dims;
self.scale_factor = renderer.get_resolution().x as f64 / new_dims.x;
self.window_dims = new_dims; 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. /// Get scaled window size.
pub fn scaled_window_size(&self) -> Vec2<f64> { self.window_dims / self.scale_factor_logical() } pub fn scaled_resolution(&self) -> Vec2<f64> { self.window_dims / self.scale_factor_logical() }
/// Get logical window size /// Get logical window size
pub fn window_size(&self) -> Vec2<f64> { self.window_dims } pub fn logical_resolution(&self) -> Vec2<f64> { self.window_dims }
/// Get physical window size
pub fn physical_resolution(&self) -> Vec2<u16> {
(self.window_dims * self.scale_factor).map(|e| e.round() as u16)
}
// Transform point from logical to scaled coordinates. // Transform point from logical to scaled coordinates.
pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> { point / self.scale_factor_logical() } pub fn scale_point(&self, point: Vec2<f64>) -> Vec2<f64> { point / self.scale_factor_logical() }

View File

@ -266,6 +266,8 @@ pub enum Event {
Close, Close,
/// The window has been resized. /// The window has been resized.
Resize(Vec2<u32>), Resize(Vec2<u32>),
/// The window scale factor has been changed
ScaleFactorChanged(f64),
/// The window has been moved. /// The window has been moved.
Moved(Vec2<u32>), Moved(Vec2<u32>),
/// A key has been typed that corresponds to a specific character. /// A key has been typed that corresponds to a specific character.
@ -656,6 +658,8 @@ impl Window {
height: logical_size.y as u32, height: logical_size.y as u32,
}, },
))); )));
self.events
.push(Event::ScaleFactorChanged(self.scale_factor));
self.needs_refresh_resize = false; self.needs_refresh_resize = false;
} }
@ -934,7 +938,8 @@ impl Window {
.push(Event::Resize(Vec2::new(width as u32, height as u32))); .push(Event::Resize(Vec2::new(width as u32, height as u32)));
}, },
WindowEvent::ScaleFactorChanged { scale_factor, .. } => { 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::ReceivedCharacter(c) => self.events.push(Event::Char(c)),
WindowEvent::MouseInput { button, state, .. } => { WindowEvent::MouseInput { button, state, .. } => {