From d139c5e6d20e293cd5977918db7a1a83f1dc90e9 Mon Sep 17 00:00:00 2001 From: Jake Go Date: Thu, 15 Jan 2026 08:55:20 -0500 Subject: [PATCH] ui: Dismiss context menus when window loses focus (#46866) Context menus close when the window becomes inactive (e.g., clicking to another window or app). Previously, menus would remain visible until the window regained focus. This aligns Zed's context menu behavior with native macOS apps, where NSMenu automatically dismisses when the window loses focus. Users expect menus to close when switching windows or apps. Uses `observe_window_activation` to detect when the window becomes inactive and calls `cancel` to dismiss the menu. Release Notes: - Fixed context menus dismiss/cancel when the window loses focus --------- Co-authored-by: Danilo Leal --- crates/ui/src/components/context_menu.rs | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs index 4372ebd821c9a935e44a4289bb377dd4af023311..97f6624d1edcb201a46ea9ef52d78b96852d45f2 100644 --- a/crates/ui/src/components/context_menu.rs +++ b/crates/ui/src/components/context_menu.rs @@ -220,6 +220,7 @@ pub struct ContextMenu { end_slot_action: Option>, key_context: SharedString, _on_blur_subscription: Subscription, + _on_window_deactivate_subscription: Subscription, keep_open_on_confirm: bool, fixed_width: Option, main_menu: Option>, @@ -295,6 +296,12 @@ impl ContextMenu { this.cancel(&menu::Cancel, window, cx) }, ); + let _on_window_deactivate_subscription = + cx.observe_window_activation(window, |this: &mut ContextMenu, window, cx| { + if !window.is_window_active() { + this.cancel(&menu::Cancel, window, cx); + } + }); window.refresh(); f( @@ -309,6 +316,7 @@ impl ContextMenu { end_slot_action: None, key_context: "menu".into(), _on_blur_subscription, + _on_window_deactivate_subscription, keep_open_on_confirm: false, fixed_width: None, main_menu: None, @@ -372,6 +380,12 @@ impl ContextMenu { this.cancel(&menu::Cancel, window, cx) }, ); + let _on_window_deactivate_subscription = + cx.observe_window_activation(window, |this: &mut ContextMenu, window, cx| { + if !window.is_window_active() { + this.cancel(&menu::Cancel, window, cx); + } + }); window.refresh(); (builder.clone())( @@ -386,6 +400,7 @@ impl ContextMenu { end_slot_action: None, key_context: "menu".into(), _on_blur_subscription, + _on_window_deactivate_subscription, keep_open_on_confirm: true, fixed_width: None, main_menu: None, @@ -455,6 +470,14 @@ impl ContextMenu { this.cancel(&menu::Cancel, window, cx) }, ), + _on_window_deactivate_subscription: cx.observe_window_activation( + window, + |this: &mut ContextMenu, window, cx| { + if !window.is_window_active() { + this.cancel(&menu::Cancel, window, cx); + } + }, + ), keep_open_on_confirm: false, fixed_width: None, main_menu: None, @@ -1206,6 +1229,12 @@ impl ContextMenu { window, |_this: &mut ContextMenu, _window, _cx| {}, ); + let _on_window_deactivate_subscription = + cx.observe_window_activation(window, |this: &mut ContextMenu, window, cx| { + if !window.is_window_active() { + this.cancel(&menu::Cancel, window, cx); + } + }); let mut menu = ContextMenu { builder: None, @@ -1218,6 +1247,7 @@ impl ContextMenu { end_slot_action: None, key_context: "menu".into(), _on_blur_subscription, + _on_window_deactivate_subscription, keep_open_on_confirm: false, fixed_width: None, documentation_aside: None,