From 382c8a1a26a18b068a796dd05d4ad253b7672826 Mon Sep 17 00:00:00 2001 From: lars-berger Date: Thu, 14 Nov 2024 19:19:30 +0800 Subject: [PATCH] feat: smoother restore from maximize into tiling/floating position (#852) --- .../events/handle_window_location_changed.rs | 29 +++++++------- .../wm/src/common/platform/native_window.rs | 38 +++++++++++++------ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/packages/wm/src/common/events/handle_window_location_changed.rs b/packages/wm/src/common/events/handle_window_location_changed.rs index 0d1a11c1..94d14a4c 100644 --- a/packages/wm/src/common/events/handle_window_location_changed.rs +++ b/packages/wm/src/common/events/handle_window_location_changed.rs @@ -30,8 +30,6 @@ pub fn handle_window_location_changed( let frame_position = try_warn!(window.native().refresh_frame_position()); - let is_minimized = try_warn!(window.native().refresh_is_minimized()); - let old_is_maximized = window.native().is_maximized()?; let is_maximized = try_warn!(window.native().refresh_is_maximized()); @@ -44,6 +42,14 @@ pub fn handle_window_location_changed( return Ok(()); } + let is_minimized = try_warn!(window.native().refresh_is_minimized()); + + // Ignore events for minimized windows. Let them be handled by the + // handler for `PlatformEvent::WindowMinimized` instead. + if is_minimized { + return Ok(()); + } + let nearest_monitor = state .nearest_monitor(&window.native()) .context("Failed to get workspace of nearest monitor.")?; @@ -71,7 +77,7 @@ pub fn handle_window_location_changed( WindowState::Fullscreen(fullscreen_state) => { // Restore the window if it's no longer fullscreen *or* for the // edge case of fullscreen -> maximized -> restore from maximized. - if !is_minimized && !(is_fullscreen || is_maximized) + if !(is_fullscreen || is_maximized) || (is_fullscreen && !is_maximized && fullscreen_state.maximized) { info!("Window restored from fullscreen."); @@ -101,13 +107,10 @@ pub fn handle_window_location_changed( } } _ => { - // Update the window to be fullscreen if there's been a change in - // maximized state or if the window is now fullscreen. - if (is_maximized && old_is_maximized != is_maximized) - || is_fullscreen - { - info!("Window fullscreened"); + if is_maximized || is_fullscreen { + info!("Window fullscreened."); + // Update the window to be fullscreen. update_window_state( window, WindowState::Fullscreen(FullscreenStateConfig { @@ -117,13 +120,7 @@ pub fn handle_window_location_changed( state, config, )?; - - // A floating window that gets minimized can hit this arm, so - // ignore such events and let it be handled by the handler for - // `PlatformEvent::WindowMinimized` instead. - } else if !is_minimized - && matches!(window.state(), WindowState::Floating(_)) - { + } else if matches!(window.state(), WindowState::Floating(_)) { // Update state with the new location of the floating window. info!("Updating floating window position."); window.set_floating_placement(frame_position); diff --git a/packages/wm/src/common/platform/native_window.rs b/packages/wm/src/common/platform/native_window.rs index 3b4fb3bb..b75df34e 100644 --- a/packages/wm/src/common/platform/native_window.rs +++ b/packages/wm/src/common/platform/native_window.rs @@ -20,13 +20,14 @@ use windows::{ EnumWindows, GetClassNameW, GetWindow, GetWindowLongPtrW, GetWindowRect, GetWindowTextW, GetWindowThreadProcessId, IsIconic, IsWindowVisible, IsZoomed, SendNotifyMessageW, - SetForegroundWindow, SetWindowLongPtrW, SetWindowPos, - ShowWindowAsync, GWL_EXSTYLE, GWL_STYLE, GW_OWNER, HWND_NOTOPMOST, - HWND_TOPMOST, SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, - SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOMOVE, SWP_NOOWNERZORDER, - SWP_NOSENDCHANGING, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, - SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWNA, WINDOW_EX_STYLE, - WINDOW_STYLE, WM_CLOSE, WS_CAPTION, WS_CHILD, WS_DLGFRAME, + SetForegroundWindow, SetWindowLongPtrW, SetWindowPlacement, + SetWindowPos, ShowWindowAsync, GWL_EXSTYLE, GWL_STYLE, GW_OWNER, + HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS, + SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOCOPYBITS, SWP_NOMOVE, + SWP_NOOWNERZORDER, SWP_NOSENDCHANGING, SWP_NOSIZE, SWP_NOZORDER, + SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOWNA, + WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WM_CLOSE, + WPF_ASYNCWINDOWPLACEMENT, WS_CAPTION, WS_CHILD, WS_DLGFRAME, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_MAXIMIZEBOX, WS_THICKFRAME, }, }, @@ -484,8 +485,21 @@ impl NativeWindow { (current_style & style.0 as isize) != 0 } - pub fn restore(&self) -> anyhow::Result<()> { - unsafe { ShowWindowAsync(HWND(self.handle), SW_RESTORE).ok() }?; + pub fn restore_to_position(&self, rect: &Rect) -> anyhow::Result<()> { + let placement = WINDOWPLACEMENT { + length: std::mem::size_of::() as u32, + flags: WPF_ASYNCWINDOWPLACEMENT, + showCmd: SW_RESTORE.0 as u32, + rcNormalPosition: RECT { + left: rect.left, + top: rect.top, + right: rect.right, + bottom: rect.bottom, + }, + ..Default::default() + }; + + unsafe { SetWindowPlacement(HWND(self.handle), &placement) }?; Ok(()) } @@ -568,7 +582,9 @@ impl NativeWindow { // to non-maximized fullscreen. WindowState::Fullscreen(config) => { if !config.maximized && self.is_maximized()? { - self.restore()?; + // Restoring to position has the same effect as `ShowWindow` with + // `SW_RESTORE`, but doesn't cause a flicker. + self.restore_to_position(rect)?; } } // No need to restore window if it'll be minimized. Transitioning @@ -576,7 +592,7 @@ impl NativeWindow { WindowState::Minimized => {} _ => { if self.is_minimized()? || self.is_maximized()? { - self.restore()?; + self.restore_to_position(rect)?; } } }