From 8e78b9fa97fe494198a94501ce93f8edb7a72851 Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Thu, 12 Mar 2026 21:16:48 +0530 Subject: [PATCH] Fix window drags when dragging button/input on titlebar in macOS (#51400) Closes https://github.com/zed-industries/zed/issues/27500 This PR fixes an issue on macOS where dragging title bar buttons and other UI elements would drag the window instead of no-op, like in native Mac apps. That made interactions like selecting text with the mouse impossible in those areas, including the title input in Rules Library. We don't want to handle this at GPUI level, since you might still want this dragging behavior while having no native titlebar for some apps, and without implementing your own handler. So, we just handle this for Zed. On macOS, we now set `is_movable: false` on all windows, which disables that drag-anything behavior and relies on the native window drag handler for window dragging instead. This also meant implementing a platform title bar for the sidebar in Rules Library, since dragging there was previously handled by the `is_movable` behavior. We already had a full-width platform title bar there on other platforms. On macOS, it is sidebar-only to keep existing design. Release Notes: - N/A --- .../src/platform_title_bar.rs | 10 +++ crates/rules_library/src/rules_library.rs | 74 ++++++++++++------- crates/zed/src/zed.rs | 2 +- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/crates/platform_title_bar/src/platform_title_bar.rs b/crates/platform_title_bar/src/platform_title_bar.rs index 1db29b0f53d9e7b185e6c3cd3029ed2e6077753e..70d24812974ee00caaad7005a593733e30788060 100644 --- a/crates/platform_title_bar/src/platform_title_bar.rs +++ b/crates/platform_title_bar/src/platform_title_bar.rs @@ -30,6 +30,7 @@ pub struct PlatformTitleBar { platform_style: PlatformStyle, children: SmallVec<[AnyElement; 2]>, should_move: bool, + background_color: Option, system_window_tabs: Entity, } @@ -43,11 +44,16 @@ impl PlatformTitleBar { platform_style, children: SmallVec::new(), should_move: false, + background_color: None, system_window_tabs, } } pub fn title_bar_color(&self, window: &mut Window, cx: &mut Context) -> Hsla { + if let Some(background_color) = self.background_color { + return background_color; + } + if cfg!(any(target_os = "linux", target_os = "freebsd")) { if window.is_window_active() && !self.should_move { cx.theme().colors().title_bar_background @@ -66,6 +72,10 @@ impl PlatformTitleBar { self.children = children.into_iter().collect(); } + pub fn set_background_color(&mut self, background_color: Option) { + self.background_color = background_color; + } + pub fn init(cx: &mut App) { SystemWindowTabs::init(cx); } diff --git a/crates/rules_library/src/rules_library.rs b/crates/rules_library/src/rules_library.rs index 73bf5fdd8fcaaf1437013d300102a9e593823c7b..dd4bbcfaeb7a14ea4bda8c546f5cf2539734eb73 100644 --- a/crates/rules_library/src/rules_library.rs +++ b/crates/rules_library/src/rules_library.rs @@ -3,9 +3,9 @@ use collections::{HashMap, HashSet}; use editor::{CompletionProvider, SelectionEffects}; use editor::{CurrentLineHighlight, Editor, EditorElement, EditorEvent, EditorStyle, actions::Tab}; use gpui::{ - App, Bounds, DEFAULT_ADDITIONAL_WINDOW_SIZE, Entity, EventEmitter, Focusable, PromptLevel, - Subscription, Task, TextStyle, Tiling, TitlebarOptions, WindowBounds, WindowHandle, - WindowOptions, actions, point, size, transparent_black, + App, Bounds, DEFAULT_ADDITIONAL_WINDOW_SIZE, Entity, EventEmitter, Focusable, MouseButton, + PromptLevel, Subscription, Task, TextStyle, Tiling, TitlebarOptions, WindowBounds, + WindowHandle, WindowOptions, actions, point, size, transparent_black, }; use language::{Buffer, LanguageRegistry, language_settings::SoftWrap}; use language_model::{ @@ -133,6 +133,7 @@ pub fn open_rules_library( window_decorations: Some(window_decorations), window_min_size: Some(DEFAULT_ADDITIONAL_WINDOW_SIZE), kind: gpui::WindowKind::Floating, + is_movable: !cfg!(target_os = "macos"), ..Default::default() }, |window, cx| { @@ -503,11 +504,7 @@ impl RulesLibrary { }); Self { - title_bar: if !cfg!(target_os = "macos") { - Some(cx.new(|cx| PlatformTitleBar::new("rules-library-title-bar", cx))) - } else { - None - }, + title_bar: Some(cx.new(|cx| PlatformTitleBar::new("rules-library-title-bar", cx))), store, language_registry, rule_editors: HashMap::default(), @@ -1129,30 +1126,44 @@ impl RulesLibrary { v_flex() .id("rule-list") .capture_action(cx.listener(Self::focus_active_rule)) - .px_1p5() .h_full() .w_64() .overflow_x_hidden() .bg(cx.theme().colors().panel_background) + .when(!cfg!(target_os = "macos"), |this| this.px_1p5()) .map(|this| { if cfg!(target_os = "macos") { - this.child( - h_flex() - .p(DynamicSpacing::Base04.rems(cx)) - .h_9() - .w_full() - .flex_none() - .justify_end() - .child( - IconButton::new("new-rule", IconName::Plus) - .tooltip(move |_window, cx| { - Tooltip::for_action("New Rule", &NewRule, cx) - }) - .on_click(|_, window, cx| { - window.dispatch_action(Box::new(NewRule), cx); - }), - ), - ) + let Some(title_bar) = self.title_bar.as_ref() else { + return this; + }; + let button_padding = DynamicSpacing::Base08.rems(cx); + let panel_background = cx.theme().colors().panel_background; + title_bar.update(cx, |title_bar, _cx| { + title_bar.set_background_color(Some(panel_background)); + title_bar.set_children(Some( + h_flex() + .w_full() + .pr(button_padding) + .justify_end() + .child( + div() + .on_mouse_down(MouseButton::Left, |_, _, cx| { + cx.stop_propagation(); + }) + .child( + IconButton::new("new-rule", IconName::Plus) + .tooltip(move |_window, cx| { + Tooltip::for_action("New Rule", &NewRule, cx) + }) + .on_click(|_, window, cx| { + window.dispatch_action(Box::new(NewRule), cx); + }), + ), + ) + .into_any_element(), + )); + }); + this.child(title_bar.clone()) } else { this.child( h_flex().p_1().w_full().child( @@ -1170,7 +1181,12 @@ impl RulesLibrary { ) } }) - .child(div().flex_grow().child(self.picker.clone())) + .child( + div() + .flex_grow() + .when(cfg!(target_os = "macos"), |this| this.px_1p5()) + .child(self.picker.clone()), + ) } fn render_active_rule_editor( @@ -1417,7 +1433,9 @@ impl Render for RulesLibrary { .overflow_hidden() .font(ui_font) .text_color(theme.colors().text) - .children(self.title_bar.clone()) + .when(!cfg!(target_os = "macos"), |this| { + this.children(self.title_bar.clone()) + }) .bg(theme.colors().background) .child( h_flex() diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b64bcbf3ab9ab5e29fdd473a200c2367e3f6f777..25defa1dde5977bd94935dafd60d97ae84b5a323 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -342,7 +342,7 @@ pub fn build_window_options(display_uuid: Option, cx: &mut App) -> WindowO focus: false, show: false, kind: WindowKind::Normal, - is_movable: true, + is_movable: !cfg!(target_os = "macos"), display_id: display.map(|display| display.id()), window_background: cx.theme().window_background_appearance(), app_id: Some(app_id.to_owned()),