diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index 8ab5b3c482..33ccb35dbe 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,6 +1,9 @@ +use std::{any::TypeId, time::Duration}; + use gpui::{ elements::*, geometry::vector::Vector2F, keymap, platform::CursorStyle, Action, AppContext, - Axis, Entity, MutableAppContext, RenderContext, SizeConstraint, View, ViewContext, + Axis, Entity, MutableAppContext, RenderContext, SizeConstraint, Subscription, View, + ViewContext, }; use menu::*; use settings::Settings; @@ -37,15 +40,22 @@ impl ContextMenuItem { fn is_separator(&self) -> bool { matches!(self, Self::Separator) } + + fn action_id(&self) -> Option { + match self { + ContextMenuItem::Item { action, .. } => Some(action.id()), + ContextMenuItem::Separator => None, + } + } } -#[derive(Default)] pub struct ContextMenu { position: Vector2F, items: Vec, selected_index: Option, visible: bool, previously_focused_view_id: Option, + _actions_observation: Subscription, } impl Entity for ContextMenu { @@ -87,15 +97,36 @@ impl View for ContextMenu { } fn on_blur(&mut self, cx: &mut ViewContext) { - self.visible = false; - self.selected_index.take(); - cx.notify(); + self.reset(cx); } } impl ContextMenu { - pub fn new() -> Self { - Default::default() + pub fn new(cx: &mut ViewContext) -> Self { + Self { + position: Default::default(), + items: Default::default(), + selected_index: Default::default(), + visible: Default::default(), + previously_focused_view_id: Default::default(), + _actions_observation: cx.observe_actions(Self::action_dispatched), + } + } + + fn action_dispatched(&mut self, action_id: TypeId, cx: &mut ViewContext) { + if let Some(ix) = self + .items + .iter() + .position(|item| item.action_id() == Some(action_id)) + { + self.selected_index = Some(ix); + cx.notify(); + cx.spawn(|this, mut cx| async move { + cx.background().timer(Duration::from_millis(100)).await; + this.update(&mut cx, |this, cx| this.cancel(&Default::default(), cx)); + }) + .detach(); + } } fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { @@ -109,12 +140,20 @@ impl ContextMenu { } fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { + self.reset(cx); if cx.handle().is_focused(cx) { let window_id = cx.window_id(); (**cx).focus(window_id, self.previously_focused_view_id.take()); } } + fn reset(&mut self, cx: &mut ViewContext) { + self.items.clear(); + self.visible = false; + self.selected_index.take(); + cx.notify(); + } + fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext) { self.selected_index = self.items.iter().position(|item| !item.is_separator()); cx.notify(); diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 9046917e39..2eab508fe0 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -172,7 +172,7 @@ impl ProjectPanel { selection: None, edit_state: None, filename_editor, - context_menu: cx.add_view(|_| ContextMenu::new()), + context_menu: cx.add_view(|cx| ContextMenu::new(cx)), }; this.update_visible_entries(None, cx); this