diff --git a/apps/wordcraft/src/main.rs b/apps/wordcraft/src/main.rs index 6402f0f2c..e29a5fd7e 100644 --- a/apps/wordcraft/src/main.rs +++ b/apps/wordcraft/src/main.rs @@ -14,7 +14,7 @@ mod graphics; mod logging; use wordcraft_engine::Session; -use wordcraft_ui_egui::{Services, UiState, WordApp, window_geometry::WindowGeometry}; +use wordcraft_ui_egui::{Services, UiState, WordApp, window_geometry::{WindowGeometry, take_rescue}}; /// The app, and the restored window geometry until the first frame has checked it. struct App(WordApp, Option); @@ -24,7 +24,7 @@ impl eframe::App for App { self.0.logic(ctx); let prev = self.0.ui.window; self.0.ui.window = ctx.input(|i| WindowGeometry::track(prev, i.viewport(), i.viewport_rect().size())); - if let Some(pos) = self.1.take().and_then(|g| ctx.input(|i| g.rescue_position(i.viewport()))) { + if let Some(pos) = ctx.input(|i| take_rescue(&mut self.1, i.viewport())) { ctx.send_viewport_cmd(egui::ViewportCommand::OuterPosition(pos)); } if self.0.quit_requested { diff --git a/crates/ui-egui/src/window_geometry.rs b/crates/ui-egui/src/window_geometry.rs index e1e3910e5..5b5e58a35 100644 --- a/crates/ui-egui/src/window_geometry.rs +++ b/crates/ui-egui/src/window_geometry.rs @@ -91,6 +91,14 @@ impl WindowGeometry { } } +/// Checks the restored geometry in `pending` once, and only once the monitor size is known: while +/// `info` doesn't report a usable one, `pending` is left in place for a later frame. Then it is taken +/// and [`WindowGeometry::rescue_position`] decides where to move the window, if anywhere. +pub fn take_rescue(pending: &mut Option, info: &egui::ViewportInfo) -> Option { + info.monitor_size.filter(|s| s.is_finite() && s.x > 0.0 && s.y > 0.0)?; + pending.take()?.rescue_position(info) +} + #[cfg(test)] mod tests { use super::*; @@ -174,6 +182,23 @@ mod tests { assert_eq!(saved().rescue_position(&egui::ViewportInfo::default()), None); } + #[test] + fn the_rescue_waits_for_the_monitor_size() { + let on = egui::Rect::from_min_size(egui::pos2(2000.0, 50.0), egui::vec2(1200.0, 800.0)); + // The first frame doesn't know the monitor yet: nothing happens and the geometry is kept. + let mut pending = Some(saved()); + assert_eq!(take_rescue(&mut pending, &egui::ViewportInfo::default()), None); + assert_eq!(pending, Some(saved())); + // A later frame reports a different monitor: centred on it, and the check is done. + assert_eq!(take_rescue(&mut pending, &info(on, egui::vec2(1600.0, 900.0))), Some(egui::pos2(200.0, 50.0))); + assert_eq!(pending, None); + assert_eq!(take_rescue(&mut pending, &info(on, egui::vec2(1024.0, 768.0))), None); + // The same monitor: no move, and the check is done as well. + let mut pending = Some(saved()); + assert_eq!(take_rescue(&mut pending, &info(on, egui::vec2(1920.0, 1080.0))), None); + assert_eq!(pending, None); + } + #[test] fn survives_the_prefs_round_trip() { let ui = crate::UiState { window: Some(saved()), ..Default::default() };