Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/browser_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)]
Expand Down
71 changes: 62 additions & 9 deletions src/win32_resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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);
}
}
Loading