diff --git a/Cargo.lock b/Cargo.lock index cb7d0e7ad6..3addc6b9fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10626,6 +10626,7 @@ dependencies = [ "story", "strum", "theme", + "windows 0.53.0", ] [[package]] diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 8c4b0081e5..64b72ba4ed 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -134,33 +134,37 @@ impl WindowsWindowInner { return false; } - fn get_titlebar_rect(&self) -> anyhow::Result { - let top_and_bottom_borders = 2; - let scale_factor = self.scale_factor.get(); - let theme = unsafe { OpenThemeData(self.hwnd, w!("WINDOW")) }; - let title_bar_size = unsafe { - GetThemePartSize( - theme, - HDC::default(), - WP_CAPTION.0, - CS_ACTIVE.0, - None, - TS_TRUE, - ) - }?; - unsafe { CloseThemeData(theme) }?; - - let mut height = - (title_bar_size.cy as f32 * scale_factor).round() as i32 + top_and_bottom_borders; + pub(crate) fn title_bar_padding(&self) -> Pixels { + // using USER_DEFAULT_SCREEN_DPI because GPUI handles the scale with the scale factor + let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) }; + px(padding as f32) + } + pub(crate) fn title_bar_top_offset(&self) -> Pixels { if self.is_maximized() { - let dpi = unsafe { GetDpiForWindow(self.hwnd) }; - height += unsafe { (GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2) as i32 }; + self.title_bar_padding() * 2 + } else { + px(0.) } + } + pub(crate) fn title_bar_height(&self) -> Pixels { + // todo(windows) this is hard set to match the ui title bar + // in the future the ui title bar component will report the size + px(32.) + self.title_bar_top_offset() + } + + pub(crate) fn caption_button_width(&self) -> Pixels { + // todo(windows) this is hard set to match the ui title bar + // in the future the ui title bar component will report the size + px(36.) + } + + fn get_titlebar_rect(&self) -> anyhow::Result { + let height = self.title_bar_height(); let mut rect = RECT::default(); unsafe { GetClientRect(self.hwnd, &mut rect) }?; - rect.bottom = rect.top + height; + rect.bottom = rect.top + ((height.0 as f32 * self.scale_factor.get()).round() as i32); Ok(rect) } @@ -923,7 +927,8 @@ impl WindowsWindowInner { let titlebar_rect = self.get_titlebar_rect(); if let Ok(titlebar_rect) = titlebar_rect { if cursor_point.y < titlebar_rect.bottom { - let caption_btn_width = unsafe { GetSystemMetricsForDpi(SM_CXSIZE, dpi) }; + let caption_btn_width = + (self.caption_button_width().0 * self.scale_factor.get()) as i32; if cursor_point.x >= titlebar_rect.right - caption_btn_width { return LRESULT(HTCLOSE as _); } else if cursor_point.x >= titlebar_rect.right - caption_btn_width * 2 { diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 3acae49d95..9fec75e17b 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -23,6 +23,9 @@ story = { workspace = true, optional = true } strum = { version = "0.25.0", features = ["derive"] } theme.workspace = true +[target.'cfg(windows)'.dependencies] +windows.workspace = true + [features] default = [] stories = ["dep:itertools", "dep:story"] diff --git a/crates/ui/src/components/title_bar/title_bar.rs b/crates/ui/src/components/title_bar/title_bar.rs index d6b3463783..8bbe9d80ec 100644 --- a/crates/ui/src/components/title_bar/title_bar.rs +++ b/crates/ui/src/components/title_bar/title_bar.rs @@ -10,12 +10,40 @@ pub struct TitleBar { content: Stateful
, children: SmallVec<[AnyElement; 2]>, } +#[cfg(not(target_os = "windows"))] +fn title_bar_top_padding(_cx: &WindowContext) -> Pixels { + px(0.) +} + +#[cfg(target_os = "windows")] +fn title_bar_top_padding(cx: &WindowContext) -> Pixels { + use windows::Win32::UI::{ + HiDpi::GetSystemMetricsForDpi, + WindowsAndMessaging::{SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI}, + }; + + // this top padding is not dependent on the title bar style and is instead a quirk of maximized windows on Windows + // https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543 + let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) }; + if cx.is_maximized() { + px((padding * 2) as f32) + } else { + px(0.) + } +} impl TitleBar { + #[cfg(not(target_os = "windows"))] pub fn height(cx: &mut WindowContext) -> Pixels { (1.75 * cx.rem_size()).max(px(32.)) } + #[cfg(target_os = "windows")] + pub fn height(_cx: &mut WindowContext) -> Pixels { + // todo(windows) instead of hard coded size report the actual size to the Windows platform API + px(32.) + } + pub fn new(id: impl Into) -> Self { Self { platform_style: PlatformStyle::platform(), @@ -29,16 +57,6 @@ impl TitleBar { self.platform_style = style; self } - - fn top_padding(&self, cx: &WindowContext) -> Pixels { - if self.platform_style == PlatformStyle::Windows && cx.is_maximized() { - // todo(windows): get padding from win32 api, need HWND from window context somehow - // should be GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2 - px(8.) - } else { - px(0.) - } - } } impl InteractiveElement for TitleBar { @@ -58,13 +76,11 @@ impl ParentElement for TitleBar { impl RenderOnce for TitleBar { fn render(self, cx: &mut WindowContext) -> impl IntoElement { let height = Self::height(cx); - let top_padding = self.top_padding(cx); - h_flex() .id("titlebar") .w_full() - .pt(top_padding) - .h(height) + .pt(title_bar_top_padding(cx)) + .h(height + title_bar_top_padding(cx)) .map(|this| { if cx.is_fullscreen() { this.pl_2() @@ -88,9 +104,7 @@ impl RenderOnce for TitleBar { .children(self.children), ) .when(self.platform_style == PlatformStyle::Windows, |title_bar| { - let button_height = Self::height(cx) - top_padding; - - title_bar.child(WindowsWindowControls::new(button_height)) + title_bar.child(WindowsWindowControls::new(height)) }) } } diff --git a/crates/ui/src/components/title_bar/windows_window_controls.rs b/crates/ui/src/components/title_bar/windows_window_controls.rs index 4f47887850..8352bed678 100644 --- a/crates/ui/src/components/title_bar/windows_window_controls.rs +++ b/crates/ui/src/components/title_bar/windows_window_controls.rs @@ -98,9 +98,11 @@ impl WindowsCaptionButton { impl RenderOnce for WindowsCaptionButton { fn render(self, _cx: &mut WindowContext) -> impl IntoElement { - // todo(windows): get padding from win32 api, need HWND from window context somehow - // should be GetSystemMetricsForDpi(SM_CXSIZE, dpi) - let width = px(36.0); + // todo(windows) report this width to the Windows platform API + // NOTE: this is intentionally hard coded. An option to use the 'native' size + // could be added when the width is reported to the Windows platform API + // as this could change between future Windows versions. + let width = px(36.); h_flex() .id(self.id)