feat(app): persist window geometry across launches
Settings gains an optional window_geometry field (size + position) serialized via #[serde(default)] so legacy settings.json files without the field deserialize cleanly to None. On launch the app restores the persisted dimensions and position; first run and pre-upgrade saves keep the existing 1280x800 centered default. settings_plugin records changes from WindowResized and WindowMoved into a PendingWindowGeometry resource and writes them to disk through the existing atomic .tmp+rename path once the events have stayed quiet for WINDOW_GEOMETRY_DEBOUNCE_SECS (0.5s). A merge_geometry helper preserves whichever component (size or position) the latest event burst didn't carry, so a position-only WindowMoved never wipes the recorded size. Pure should_persist_geometry and merge_geometry helpers are unit tested for the boundary cases. Headless integration tests cover the full flow: a single resize event then a quiet window persists, a move event after a resize updates only position, a rapid storm collapses to the final size, and a quiet frame with no events leaves the geometry untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,21 @@ fn main() {
|
||||
.unwrap_or_default();
|
||||
let sync_provider = provider_for_backend(&settings.sync_backend);
|
||||
|
||||
// Restore the previous window geometry if the player has one saved.
|
||||
// Otherwise open at the platform default (1280×800, centred on the
|
||||
// primary monitor). The window_geometry field is None on first run
|
||||
// and after upgrading from a build that didn't persist geometry.
|
||||
let (window_resolution, window_position) = match settings.window_geometry {
|
||||
Some(geom) => (
|
||||
(geom.width, geom.height).into(),
|
||||
WindowPosition::At(IVec2::new(geom.x, geom.y)),
|
||||
),
|
||||
None => (
|
||||
(1280u32, 800u32).into(),
|
||||
WindowPosition::Centered(MonitorSelection::Primary),
|
||||
),
|
||||
};
|
||||
|
||||
App::new()
|
||||
.add_plugins(
|
||||
DefaultPlugins
|
||||
@@ -48,8 +63,8 @@ fn main() {
|
||||
// X11/Wayland WM_CLASS so taskbar managers group
|
||||
// multiple windows of this app correctly.
|
||||
name: Some("solitaire-quest".into()),
|
||||
resolution: (1280u32, 800u32).into(),
|
||||
position: WindowPosition::Centered(MonitorSelection::Primary),
|
||||
resolution: window_resolution,
|
||||
position: window_position,
|
||||
// AutoNoVsync prefers Mailbox (triple-buffered) and
|
||||
// falls back to Immediate, eliminating the vsync stall
|
||||
// that AutoVsync produces during continuous window
|
||||
|
||||
Reference in New Issue
Block a user