diff --git a/Cargo.lock b/Cargo.lock index a103488028..b557431d80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6088,6 +6088,7 @@ dependencies = [ "collections", "command_palette", "contacts_panel", + "context_menu", "ctor", "diagnostics", "dirs 3.0.1", diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index ff1c3dda72..de4f05cade 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,9 +1,18 @@ use gpui::{ - elements::*, geometry::vector::Vector2F, platform::CursorStyle, Action, Axis, Entity, - RenderContext, SizeConstraint, View, ViewContext, + elements::*, geometry::vector::Vector2F, impl_internal_actions, platform::CursorStyle, Action, + Axis, Entity, MutableAppContext, RenderContext, SizeConstraint, View, ViewContext, }; use settings::Settings; +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(ContextMenu::dismiss); +} + +#[derive(Clone)] +struct Dismiss; + +impl_internal_actions!(context_menu, [Dismiss]); + pub enum ContextMenuItem { Item { label: String, @@ -25,11 +34,13 @@ impl ContextMenuItem { } } +#[derive(Default)] pub struct ContextMenu { position: Vector2F, items: Vec, selected_index: Option, visible: bool, + previously_focused_view_id: Option, } impl Entity for ContextMenu { @@ -72,11 +83,13 @@ impl View for ContextMenu { impl ContextMenu { pub fn new() -> Self { - Self { - position: Default::default(), - items: Default::default(), - selected_index: Default::default(), - visible: false, + Default::default() + } + + fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext) { + if cx.handle().is_focused(cx) { + let window_id = cx.window_id(); + (**cx).focus(window_id, self.previously_focused_view_id.take()); } } @@ -87,11 +100,15 @@ impl ContextMenu { cx: &mut ViewContext, ) { let mut items = items.into_iter().peekable(); - assert!(items.peek().is_some(), "must have at least one item"); - self.items = items.collect(); - self.position = position; - self.visible = true; - cx.focus_self(); + if items.peek().is_some() { + self.items = items.collect(); + self.position = position; + self.visible = true; + self.previously_focused_view_id = cx.focused_view_id(cx.window_id()); + cx.focus_self(); + } else { + self.visible = false; + } cx.notify(); } @@ -107,7 +124,10 @@ impl ContextMenu { &Default::default(), Some(ix) == self.selected_index, ); - Label::new(label.to_string(), style.label.clone()).boxed() + Label::new(label.to_string(), style.label.clone()) + .contained() + .with_style(style.container) + .boxed() } ContextMenuItem::Separator => Empty::new() .collapsed() @@ -180,7 +200,10 @@ impl ContextMenu { .boxed() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(move |_, _, cx| cx.dispatch_any_action(action.boxed_clone())) + .on_click(move |_, _, cx| { + cx.dispatch_any_action(action.boxed_clone()); + cx.dispatch_action(Dismiss); + }) .boxed() } ContextMenuItem::Separator => Empty::new() diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index d11940b2c6..6c66771082 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -2407,7 +2407,7 @@ impl MutableAppContext { }) } - fn focus(&mut self, window_id: usize, view_id: Option) { + pub fn focus(&mut self, window_id: usize, view_id: Option) { if let Some(pending_focus_index) = self.pending_focus_index { self.pending_effects.remove(pending_focus_index); } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 525569b869..97a50e78d2 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -22,6 +22,7 @@ chat_panel = { path = "../chat_panel" } cli = { path = "../cli" } collections = { path = "../collections" } command_palette = { path = "../command_palette" } +context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } contacts_panel = { path = "../contacts_panel" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3e21e454f2..04a4f9aada 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -134,6 +134,7 @@ fn main() { let mut languages = languages::build_language_registry(login_shell_env_loaded); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); + context_menu::init(cx); auto_update::init(http, client::ZED_SERVER_URL.clone(), cx); project::Project::init(&client); client::Channel::init(&client);