From a275ad0b1401d57a4220792c16b7fc32c8b3aab4 Mon Sep 17 00:00:00 2001 From: zhuxiaojt Date: Sun, 28 Jun 2026 15:37:43 +0800 Subject: [PATCH] fix: install resize border when set decorations --- src/browser_window.rs | 10 ++++++ src/win32_resize.rs | 71 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index 78d2730..75c07a3 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -779,6 +779,8 @@ impl BrowserWindow { #[napi] pub fn set_resizable(&self, resizable: bool) { self.window.set_resizable(resizable); + // Note: resize border installation is managed by decoration state only. + // The resizable flag controls whether the window actually responds to resize attempts. } #[napi] @@ -1632,6 +1634,14 @@ impl BrowserWindow { #[napi] pub fn set_decorations(&self, enabled: bool) { self.window.set_decorations(enabled); + + // On Windows, when switching to undecorated mode with resizable window, + // install the resize border subclass for proper edge resizing support. + // This must be done AFTER set_decorations because it may remove WS_THICKFRAME. + #[cfg(target_os = "windows")] + if !enabled && self.window.is_resizable() { + crate::win32_resize::install_resize_border(&self.window); + } } #[napi(getter)] diff --git a/src/win32_resize.rs b/src/win32_resize.rs index e5422c3..0878215 100644 --- a/src/win32_resize.rs +++ b/src/win32_resize.rs @@ -25,6 +25,12 @@ extern "system" { dw_ref_data: usize, ) -> BOOL; + fn RemoveWindowSubclass( + hwnd: HWND, + pfn_subclass: SubclassProc, + uid_subclass: usize, + ) -> BOOL; + fn DefSubclassProc(hwnd: HWND, umsg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT; } @@ -36,11 +42,20 @@ unsafe extern "system" fn subclass_proc( _uid: usize, _dw_ref_data: usize, ) -> LRESULT { - // WM_NCCALCSIZE: collapse the non-client area so the client rect fills the - // entire window. This keeps the window visually borderless while - // WS_THICKFRAME remains in the style (required for drag_resize_window). + // WM_NCCALCSIZE: collapse the non-client area but keep 1px border for system shadow. + // This keeps WS_THICKFRAME in the style (required for drag_resize_window) while + // maintaining the drop shadow around the window. if msg == WM_NCCALCSIZE && wparam != 0 { - return 0; + use windows_sys::Win32::UI::WindowsAndMessaging::NCCALCSIZE_PARAMS; + let params = &mut *(lparam as *mut NCCALCSIZE_PARAMS); + + // Keep 1px on all sides for shadow, but remove the rest of the non-client area + params.rgrc[0].left += 1; + params.rgrc[0].top += 1; + params.rgrc[0].right -= 1; + params.rgrc[0].bottom -= 1; + + return 0; // Use our calculated client area } DefSubclassProc(hwnd, msg, wparam, lparam) @@ -61,14 +76,53 @@ pub fn install_resize_border(window: &winit::window::Window) { unsafe { let style = GetWindowLongPtrW(hwnd, GWL_STYLE); - // LONG_PTR is i32 on 32-bit and isize on 64-bit; use usize as a - // common intermediary for the bit-OR, then let `as _` coerce back. + + // Only add WS_THICKFRAME if not already present + if (style as usize & WS_THICKFRAME as usize) == 0 { + SetWindowLongPtrW( + hwnd, + GWL_STYLE, + (style as usize | WS_THICKFRAME as usize) as _, + ); + // Force a frame recalculation so WS_THICKFRAME takes effect immediately. + SetWindowPos( + hwnd, + 0, + 0, + 0, + 0, + 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED, + ); + } + + // Always ensure the subclass is installed (SetWindowSubclass is idempotent with same uid) + SetWindowSubclass(hwnd, subclass_proc, 1, 0); + } +} + +/// Remove the resize border subclass and WS_THICKFRAME when window becomes non-resizable. +pub fn uninstall_resize_border(window: &winit::window::Window) { + let Ok(handle) = window.window_handle() else { + return; + }; + let RawWindowHandle::Win32(h) = handle.as_raw() else { + return; + }; + let hwnd = h.hwnd.get() as HWND; + + unsafe { + // Remove the subclass + RemoveWindowSubclass(hwnd, subclass_proc, 1); + + // Remove WS_THICKFRAME + let style = GetWindowLongPtrW(hwnd, GWL_STYLE); SetWindowLongPtrW( hwnd, GWL_STYLE, - (style as usize | WS_THICKFRAME as usize) as _, + (style as usize & !(WS_THICKFRAME as usize)) as _, ); - // Force a frame recalculation so WS_THICKFRAME takes effect immediately. + // Force a frame recalculation SetWindowPos( hwnd, 0, @@ -78,6 +132,5 @@ pub fn install_resize_border(window: &winit::window::Window) { 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED, ); - SetWindowSubclass(hwnd, subclass_proc, 1, 0); } }