diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 583ff1a96b..c3ddfdbfbd 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -82,7 +82,7 @@ pub(crate) fn handle_msg( WM_IME_STARTCOMPOSITION => handle_ime_position(handle, state_ptr), WM_IME_COMPOSITION => handle_ime_composition(handle, lparam, state_ptr), WM_SETCURSOR => handle_set_cursor(lparam, state_ptr), - WM_SETTINGCHANGE => handle_system_settings_changed(state_ptr), + WM_SETTINGCHANGE => handle_system_settings_changed(handle, state_ptr), CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr), _ => None, }; @@ -732,7 +732,10 @@ fn handle_dpi_changed_msg( state_ptr: Rc, ) -> Option { let new_dpi = wparam.loword() as f32; - state_ptr.state.borrow_mut().scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32; + let mut lock = state_ptr.state.borrow_mut(); + lock.scale_factor = new_dpi / USER_DEFAULT_SCREEN_DPI as f32; + lock.border_offset.udpate(handle).log_err(); + drop(lock); let rect = unsafe { &*(lparam.0 as *const RECT) }; let width = rect.right - rect.left; @@ -1050,12 +1053,17 @@ fn handle_set_cursor(lparam: LPARAM, state_ptr: Rc) -> Op Some(1) } -fn handle_system_settings_changed(state_ptr: Rc) -> Option { +fn handle_system_settings_changed( + handle: HWND, + state_ptr: Rc, +) -> Option { let mut lock = state_ptr.state.borrow_mut(); // mouse wheel lock.system_settings.mouse_wheel_settings.update(); // mouse double click lock.click_state.system_update(); + // window border offset + lock.border_offset.udpate(handle).log_err(); Some(0) } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index e701fb1b1c..536894037e 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -35,6 +35,7 @@ pub struct WindowsWindowState { pub origin: Point, pub logical_size: Size, pub fullscreen_restore_bounds: Bounds, + pub border_offset: WindowBorderOffset, pub scale_factor: f32, pub callbacks: Callbacks, @@ -82,6 +83,7 @@ impl WindowsWindowState { origin, size: logical_size, }; + let border_offset = WindowBorderOffset::default(); let renderer = windows_renderer::windows_renderer(hwnd, transparent); let callbacks = Callbacks::default(); let input_handler = None; @@ -94,6 +96,7 @@ impl WindowsWindowState { origin, logical_size, fullscreen_restore_bounds, + border_offset, scale_factor, callbacks, input_handler, @@ -124,7 +127,8 @@ impl WindowsWindowState { } } - fn window_bounds(&self) -> WindowBounds { + // Calculate the bounds used for saving and whether the window is maximized. + fn calculate_window_bounds(&self) -> (Bounds, bool) { let placement = unsafe { let mut placement = WINDOWPLACEMENT { length: std::mem::size_of::() as u32, @@ -133,22 +137,22 @@ impl WindowsWindowState { GetWindowPlacement(self.hwnd, &mut placement).log_err(); placement }; - let physical_size = size( - DevicePixels(placement.rcNormalPosition.right - placement.rcNormalPosition.left), - DevicePixels(placement.rcNormalPosition.bottom - placement.rcNormalPosition.top), - ); - let bounds = Bounds { - origin: logical_point( - placement.rcNormalPosition.left as f32, - placement.rcNormalPosition.top as f32, + ( + calculate_client_rect( + placement.rcNormalPosition, + self.border_offset, self.scale_factor, ), - size: physical_size.to_pixels(self.scale_factor), - }; + placement.showCmd == SW_SHOWMAXIMIZED.0 as u32, + ) + } + + fn window_bounds(&self) -> WindowBounds { + let (bounds, maximized) = self.calculate_window_bounds(); if self.is_fullscreen() { WindowBounds::Fullscreen(self.fullscreen_restore_bounds) - } else if placement.showCmd == SW_SHOWMAXIMIZED.0 as u32 { + } else if maximized { WindowBounds::Maximized(bounds) } else { WindowBounds::Windowed(bounds) @@ -308,7 +312,6 @@ impl WindowsWindow { }; let state_ptr = Rc::clone(context.inner.as_ref().unwrap()); register_drag_drop(state_ptr.clone()); - let wnd = Self(state_ptr); unsafe { let mut placement = WINDOWPLACEMENT { @@ -322,13 +325,16 @@ impl WindowsWindow { } else { display.default_bounds() }; - let bounds = bounds.to_device_pixels(wnd.0.state.borrow().scale_factor); - placement.rcNormalPosition = calcualte_window_position(bounds, raw_hwnd).unwrap(); + let mut lock = state_ptr.state.borrow_mut(); + let bounds = bounds.to_device_pixels(lock.scale_factor); + lock.border_offset.udpate(raw_hwnd).unwrap(); + placement.rcNormalPosition = calcualte_window_rect(bounds, lock.border_offset); + drop(lock); SetWindowPlacement(raw_hwnd, &placement).log_err(); } unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok().log_err() }; - wnd + Self(state_ptr) } } @@ -544,10 +550,6 @@ impl PlatformWindow for WindowsWindow { .executor .spawn(async move { let mut lock = state_ptr.state.borrow_mut(); - lock.fullscreen_restore_bounds = Bounds { - origin: lock.origin, - size: lock.logical_size, - }; let StyleAndBounds { style, x, @@ -557,6 +559,8 @@ impl PlatformWindow for WindowsWindow { } = if let Some(state) = lock.fullscreen.take() { state } else { + let (window_bounds, _) = lock.calculate_window_bounds(); + lock.fullscreen_restore_bounds = window_bounds; let style = WINDOW_STYLE(unsafe { get_window_long(state_ptr.hwnd, GWL_STYLE) } as _); let mut rc = RECT::default(); @@ -861,6 +865,32 @@ struct StyleAndBounds { cy: i32, } +#[derive(Debug, Default, Clone, Copy)] +pub(crate) struct WindowBorderOffset { + width_offset: i32, + height_offset: i32, +} + +impl WindowBorderOffset { + pub(crate) fn udpate(&mut self, hwnd: HWND) -> anyhow::Result<()> { + let window_rect = unsafe { + let mut rect = std::mem::zeroed(); + GetWindowRect(hwnd, &mut rect)?; + rect + }; + let client_rect = unsafe { + let mut rect = std::mem::zeroed(); + GetClientRect(hwnd, &mut rect)?; + rect + }; + self.width_offset = + (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left); + self.height_offset = + (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top); + Ok(()) + } +} + fn register_wnd_class(icon_handle: HICON) -> PCWSTR { const CLASS_NAME: PCWSTR = w!("Zed::Window"); @@ -955,36 +985,49 @@ fn register_drag_drop(state_ptr: Rc) { }; } -fn calcualte_window_position(bounds: Bounds, hwnd: HWND) -> anyhow::Result { +fn calcualte_window_rect(bounds: Bounds, border_offset: WindowBorderOffset) -> RECT { + // NOTE: + // The reason that not using `AdjustWindowRectEx()` here is + // that the size reported by this function is incorrect. + // You can test it, and there are similar discussions online. + // See: https://stackoverflow.com/questions/12423584/how-to-set-exact-client-size-for-overlapped-window-winapi + // + // So we manually calculate these values here. let mut rect = RECT { left: bounds.left().0, top: bounds.top().0, right: bounds.right().0, bottom: bounds.bottom().0, }; - let window_rect = unsafe { - let mut rect = std::mem::zeroed(); - GetWindowRect(hwnd, &mut rect)?; - rect - }; - let client_rect = unsafe { - let mut rect = std::mem::zeroed(); - GetClientRect(hwnd, &mut rect)?; - rect - }; - let width_offset = - (window_rect.right - window_rect.left) - (client_rect.right - client_rect.left); - let height_offset = - (window_rect.bottom - window_rect.top) - (client_rect.bottom - client_rect.top); - let left_offset = width_offset / 2; - let top_offset = height_offset / 2; - let right_offset = width_offset - left_offset; - let bottom_offet = height_offset - top_offset; + let left_offset = border_offset.width_offset / 2; + let top_offset = border_offset.height_offset / 2; + let right_offset = border_offset.width_offset - left_offset; + let bottom_offet = border_offset.height_offset - top_offset; rect.left -= left_offset; rect.top -= top_offset; rect.right += right_offset; rect.bottom += bottom_offet; - Ok(rect) + rect +} + +fn calculate_client_rect( + rect: RECT, + border_offset: WindowBorderOffset, + scale_factor: f32, +) -> Bounds { + let left_offset = border_offset.width_offset / 2; + let top_offset = border_offset.height_offset / 2; + let right_offset = border_offset.width_offset - left_offset; + let bottom_offet = border_offset.height_offset - top_offset; + let left = rect.left + left_offset; + let top = rect.top + top_offset; + let right = rect.right - right_offset; + let bottom = rect.bottom - bottom_offet; + let physical_size = size(DevicePixels(right - left), DevicePixels(bottom - top)); + Bounds { + origin: logical_point(left as f32, top as f32, scale_factor), + size: physical_size.to_pixels(scale_factor), + } } // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-dragqueryfilew