From 736ff08e9014e8bf11225df9de16700f76e9874e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 15 May 2026 23:12:43 -0400 Subject: [PATCH] Fix dead traffic-light hit area under FullSizeContentViewWindowMask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `move_traffic_light` was framing the close / minimize / zoom NSButtons at `titlebar_height - traffic_light_position.y - button_h`. Once the window opts into `NSFullSizeContentViewWindowMask`, the content layout rectangle covers the whole frame and `titlebar_height()` returns 0 — collapsing the origin to a large negative Y. macOS still painted the buttons through its own caching layer at the title bar's natural position, but the buttons' hit-test rectangle followed the frame off screen, so clicking close / minimize / zoom did nothing. Anchor the math against the close button's actual superview frame height (the themeFrame, which equals the window height in Y-up coordinates) so the visual position and the hit area stay in lockstep no matter what mask combination the window opens with. `titlebar_height` stays available for callers that want the legacy non-full-size interpretation, with `#[allow(dead_code)]` and a docstring pointing at this fix. --- third_party/gpui/src/platform/mac/window.rs | 32 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/third_party/gpui/src/platform/mac/window.rs b/third_party/gpui/src/platform/mac/window.rs index 95efffa..708783f 100644 --- a/third_party/gpui/src/platform/mac/window.rs +++ b/third_party/gpui/src/platform/mac/window.rs @@ -430,8 +430,6 @@ impl MacWindowState { return; } - let titlebar_height = self.titlebar_height(); - unsafe { let close_button: id = msg_send![ self.native_window, @@ -449,9 +447,27 @@ impl MacWindowState { let mut close_button_frame: CGRect = msg_send![close_button, frame]; let mut min_button_frame: CGRect = msg_send![min_button, frame]; let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame]; + + // The buttons live inside the window's themeFrame, which + // is Y-up and shares its height with the window itself. + // Using `self.titlebar_height()` here returns 0 once + // `NSFullSizeContentViewWindowMask` is set (the content + // layout fills the whole window), so the previous + // `titlebar_height - y - button_h` math collapsed the + // origin to a large negative number — macOS still + // painted the buttons through a separate caching layer, + // but the hit-test rectangle followed the frame off + // screen and clicks stopped reaching close / minimize / + // zoom. Anchor against the button's real superview + // instead so the frame and the visual position stay in + // lockstep. + let close_super: id = msg_send![close_button, superview]; + let super_frame: CGRect = msg_send![close_super, frame]; + let super_height = px(super_frame.size.height as f32); + let mut origin = point( traffic_light_position.x, - titlebar_height + super_height - traffic_light_position.y - px(close_button_frame.size.height as f32), ); @@ -546,6 +562,16 @@ impl MacWindowState { get_scale_factor(self.native_window) } + /// Reports the height of the title bar's NSWindow chrome, i.e. the + /// vertical band that NSWindow steals from the window frame for + /// the system title and traffic-light controls. Returns 0 with + /// `NSFullSizeContentViewWindowMask` (the content layout fills the + /// whole frame), which is exactly why `move_traffic_light` cannot + /// rely on this value to position custom traffic lights — see the + /// long-form comment there. Kept for callers that want the legacy + /// "non-full-size" interpretation; do not reach for it from new + /// chrome-positioning code. + #[allow(dead_code)] fn titlebar_height(&self) -> Pixels { unsafe { let frame = NSWindow::frame(self.native_window);