From 70d99086de3a58189d65c49954a3495972880725 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 25 Jan 2024 01:48:46 +0200 Subject: [PATCH] fix(window-state): restore window position if the one of the window corners intersects with monitor (#898) * fix(window-state): restore window positions that matches monitor positions closes #892 * check for intersections of any window corner instead of only top-left --- .changes/window-state-zero-positions.md | 8 +++ plugins/window-state/src/lib.rs | 82 +++++++++++++++++++------ 2 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 .changes/window-state-zero-positions.md diff --git a/.changes/window-state-zero-positions.md b/.changes/window-state-zero-positions.md new file mode 100644 index 000000000..14e89087c --- /dev/null +++ b/.changes/window-state-zero-positions.md @@ -0,0 +1,8 @@ +--- +"window-state": "patch" +--- + +Address a couple of issues with restoring positions: + +- Fix restoring window positions correctly when the top-left corner of the window was outside of the monitor. +- Fix restore maximization state only maximized on main monitor. diff --git a/plugins/window-state/src/lib.rs b/plugins/window-state/src/lib.rs index f17201caa..30dac7008 100644 --- a/plugins/window-state/src/lib.rs +++ b/plugins/window-state/src/lib.rs @@ -59,6 +59,11 @@ struct WindowState { height: f64, x: i32, y: i32, + // prev_x and prev_y are used to store position + // before maximization happened, because maximization + // will set x and y to the top-left corner of the monitor + prev_x: i32, + prev_y: i32, maximized: bool, visible: bool, decorated: bool, @@ -72,6 +77,8 @@ impl Default for WindowState { height: Default::default(), x: Default::default(), y: Default::default(), + prev_x: Default::default(), + prev_y: Default::default(), maximized: Default::default(), visible: true, decorated: true, @@ -141,13 +148,23 @@ impl WindowExt for Window { } if flags.contains(StateFlags::POSITION) { + let position = (state.x, state.y).into(); + let size = (state.width, state.height).into(); // restore position to saved value if saved monitor exists // otherwise, let the OS decide where to place the window for m in self.available_monitors()? { - if m.contains((state.x, state.y).into()) { + if m.intersects(position, size) { self.set_position(PhysicalPosition { - x: state.x, - y: state.y, + x: if state.maximized { + state.prev_x + } else { + state.x + }, + y: if state.maximized { + state.prev_y + } else { + state.y + }, })?; } } @@ -243,22 +260,17 @@ impl WindowExtInternal for Window { .unwrap_or(1.); let size = self.inner_size()?.to_logical(scale_factor); - // It doesn't make sense to save a self with 0 height or width + // It doesn't make sense to save a window with 0 height or width if size.width > 0. && size.height > 0. && !is_maximized { state.width = size.width; state.height = size.height; } } - if flags.contains(StateFlags::POSITION) { + if flags.contains(StateFlags::POSITION) && !is_maximized { let position = self.outer_position()?; - if let Ok(Some(monitor)) = self.current_monitor() { - // save only window positions that are inside the current monitor - if monitor.contains(position) && !is_maximized { - state.x = position.x; - state.y = position.y; - } - } + state.x = position.x; + state.y = position.y; } Ok(()) @@ -273,6 +285,10 @@ pub struct Builder { } impl Builder { + pub fn new() -> Self { + Self::default() + } + /// Sets the state flags to control what state gets restored and saved. pub fn with_state_flags(mut self, flags: StateFlags) -> Self { self.state_flags = flags; @@ -345,13 +361,25 @@ impl Builder { .or_insert_with(WindowState::default); } - window.on_window_event(move |e| { - if let WindowEvent::CloseRequested { .. } = e { + window.on_window_event(move |e| match e { + WindowEvent::CloseRequested { .. } => { let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { let _ = window_clone.update_state(state, flags); } } + + WindowEvent::Moved(position) if flags.contains(StateFlags::POSITION) => { + let mut c = cache.lock().unwrap(); + if let Some(state) = c.get_mut(&label) { + state.prev_x = state.x; + state.prev_y = state.y; + + state.x = position.x; + state.y = position.y; + } + } + _ => {} }); }) .on_event(move |app, event| { @@ -364,17 +392,31 @@ impl Builder { } trait MonitorExt { - fn contains(&self, position: PhysicalPosition) -> bool; + fn intersects(&self, position: PhysicalPosition, size: LogicalSize) -> bool; } impl MonitorExt for Monitor { - fn contains(&self, position: PhysicalPosition) -> bool { + fn intersects(&self, position: PhysicalPosition, size: LogicalSize) -> bool { + let size = size.to_physical::(self.scale_factor()); + let PhysicalPosition { x, y } = *self.position(); let PhysicalSize { width, height } = *self.size(); - x < position.x as _ - && position.x < (x + width as i32) - && y < position.y as _ - && position.y < (y + height as i32) + let left = x; + let right = x + width as i32; + let top = y; + let bottom = y + height as i32; + + [ + (position.x, position.y), + (position.x + size.width as i32, position.y), + (position.x, position.y + size.height as i32), + ( + position.x + size.width as i32, + position.y + size.height as i32, + ), + ] + .into_iter() + .any(|(x, y)| x >= left && x < right && y >= top && y < bottom) } }