windows: Return client size and position from window_bounds (#14228)

This is a follow up of #14218 , since we open the window based on the
size of the client area, `window_bounds` should also return the size of
the client area to maintain consistency.

Release Notes:

- N/A
This commit is contained in:
张小白 2024-07-13 04:19:36 +08:00 committed by GitHub
parent 85d77a3eec
commit 0a718c65e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 94 additions and 43 deletions

View file

@ -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<WindowsWindowStatePtr>,
) -> Option<isize> {
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<WindowsWindowStatePtr>) -> Op
Some(1)
}
fn handle_system_settings_changed(state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
fn handle_system_settings_changed(
handle: HWND,
state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> {
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)
}

View file

@ -35,6 +35,7 @@ pub struct WindowsWindowState {
pub origin: Point<Pixels>,
pub logical_size: Size<Pixels>,
pub fullscreen_restore_bounds: Bounds<Pixels>,
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<Pixels>, bool) {
let placement = unsafe {
let mut placement = WINDOWPLACEMENT {
length: std::mem::size_of::<WINDOWPLACEMENT>() 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<WindowsWindowStatePtr>) {
};
}
fn calcualte_window_position(bounds: Bounds<DevicePixels>, hwnd: HWND) -> anyhow::Result<RECT> {
fn calcualte_window_rect(bounds: Bounds<DevicePixels>, 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<Pixels> {
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