From 3755f3da8c7844df646eaf6da5b80d38f04a3dce Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Sat, 9 May 2026 09:26:14 +0200 Subject: [PATCH 1/2] fix(window-state): convert macOS coordinates to Logical to fix overlay shrink and multi-monitor DPI scaling drift --- plugins/window-state/src/lib.rs | 94 +++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/plugins/window-state/src/lib.rs b/plugins/window-state/src/lib.rs index 42c0fdf6dc..e668795740 100644 --- a/plugins/window-state/src/lib.rs +++ b/plugins/window-state/src/lib.rs @@ -187,33 +187,29 @@ 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 + let logical_pos = tauri::LogicalPosition::new(state.x, state.y); + let logical_size = tauri::LogicalSize::new(state.width, state.height); + for m in self.available_monitors()? { - if m.intersects(position, size) { - self.set_position(PhysicalPosition { - x: if state.maximized { - state.prev_x - } else { - state.x - }, - y: if state.maximized { - state.prev_y - } else { - state.y - }, - })?; + let scale = m.scale_factor(); + let phys_pos = logical_pos.to_physical::(scale); + let phys_size = logical_size.to_physical::(scale); + + if m.intersects(phys_pos, phys_size) { + let x_pos = if state.maximized { state.prev_x } else { state.x }; + let y_pos = if state.maximized { state.prev_y } else { state.y }; + + self.set_position(tauri::Position::Logical(tauri::LogicalPosition::new(x_pos as f64, y_pos as f64).into()))?; + break; } } } if flags.contains(StateFlags::SIZE) { - self.set_size(PhysicalSize { - width: state.width, - height: state.height, - })?; + self.set_size(tauri::Size::Logical(tauri::LogicalSize::new( + state.width as f64, + state.height as f64, + ).into()))?; } if flags.contains(StateFlags::MAXIMIZED) && state.maximized { @@ -229,13 +225,15 @@ impl WindowExt for Window { let mut metadata = WindowState::default(); if flags.contains(StateFlags::SIZE) { - let size = self.inner_size()?; + let scale = self.scale_factor().unwrap_or(1.0); + let size = self.inner_size()?.to_logical::(scale); metadata.width = size.width; metadata.height = size.height; } if flags.contains(StateFlags::POSITION) { - let pos = self.outer_position()?; + let scale = self.scale_factor().unwrap_or(1.0); + let pos = self.outer_position()?.to_logical::(scale); metadata.x = pos.x; metadata.y = pos.y; } @@ -303,16 +301,30 @@ impl WindowExtInternal for Window { } if flags.contains(StateFlags::SIZE) && !is_maximized && !is_minimized { - let size = self.inner_size()?; - // It doesn't make sense to save a window with 0 height or width - if size.width > 0 && size.height > 0 { - state.width = size.width; - state.height = size.height; + let scale = self.scale_factor().unwrap_or(1.0); + let mut logical_size = self.inner_size()?.to_logical::(scale); + + #[cfg(target_os = "macos")] + { + if let Ok(outer) = self.outer_size() { + let outer_logical = outer.to_logical::(scale); + let diff = outer_logical.height.saturating_sub(logical_size.height); + if diff >= 20 && diff <= 40 { + logical_size.height = outer_logical.height; + logical_size.width = outer_logical.width; + } + } + } + + if logical_size.width > 0 && logical_size.height > 0 { + state.width = logical_size.width; + state.height = logical_size.height; } } if flags.contains(StateFlags::POSITION) && !is_maximized && !is_minimized { - let position = self.outer_position()?; + let scale = self.scale_factor().unwrap_or(1.0); + let position = self.outer_position()?.to_logical::(scale); state.x = position.x; state.y = position.y; } @@ -462,11 +474,13 @@ impl Builder { { let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { + let scale = window_clone.scale_factor().unwrap_or(1.0); + let logical_pos = position.to_logical::(scale); state.prev_x = state.x; state.prev_y = state.y; - state.x = position.x; - state.y = position.y; + state.x = logical_pos.x; + state.y = logical_pos.y; } } } @@ -490,8 +504,22 @@ impl Builder { if !window_clone.is_minimized().unwrap_or_default() && !is_maximized { let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { - state.width = size.width; - state.height = size.height; + let scale = window_clone.scale_factor().unwrap_or(1.0); + let mut logical_size = size.to_logical::(scale); + + #[cfg(target_os = "macos")] + { + if let Ok(outer) = window_clone.outer_size() { + let outer_logical = outer.to_logical::(scale); + let diff = outer_logical.height.saturating_sub(logical_size.height); + if diff >= 20 && diff <= 40 { + logical_size.height = outer_logical.height; + logical_size.width = outer_logical.width; + } + } + } + state.width = logical_size.width; + state.height = logical_size.height; } } } From a0b839ba86d7373bb7d05277691404f7902da74c Mon Sep 17 00:00:00 2001 From: Grzegorz Kucmierz Date: Sat, 9 May 2026 09:40:58 +0200 Subject: [PATCH 2/2] chore: add changeset for logical coordinates and macOS overlay fix --- .changes/window-state-macos-logical-coordinates.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/window-state-macos-logical-coordinates.md diff --git a/.changes/window-state-macos-logical-coordinates.md b/.changes/window-state-macos-logical-coordinates.md new file mode 100644 index 0000000000..3a200d250c --- /dev/null +++ b/.changes/window-state-macos-logical-coordinates.md @@ -0,0 +1,6 @@ +--- +"tauri-plugin-window-state": patch +"@tauri-apps/plugin-window-state": patch +--- + +Refactored state restoration and window caching to use `Logical` coordinates instead of `Physical`, resolving scaling drift and position anomalies across multi-monitor setups on macOS. Implemented a specific heuristic to compensate for `titleBarStyle: Overlay` causing cascading window shrinkage.