diff --git a/Cargo.lock b/Cargo.lock index b557431d80..f39f473ccc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -665,6 +665,7 @@ dependencies = [ "client", "editor", "gpui", + "menu", "postage", "settings", "theme", @@ -964,6 +965,7 @@ dependencies = [ "gpui", "language", "log", + "menu", "picker", "postage", "project", @@ -979,6 +981,7 @@ name = "context_menu" version = "0.1.0" dependencies = [ "gpui", + "menu", "settings", "smallvec", "theme", @@ -1526,6 +1529,7 @@ dependencies = [ "env_logger", "fuzzy", "gpui", + "menu", "picker", "postage", "project", @@ -1904,6 +1908,7 @@ version = "0.1.0" dependencies = [ "editor", "gpui", + "menu", "postage", "settings", "text", @@ -2698,6 +2703,13 @@ dependencies = [ "autocfg 1.0.1", ] +[[package]] +name = "menu" +version = "0.1.0" +dependencies = [ + "gpui", +] + [[package]] name = "metal" version = "0.21.0" @@ -3252,6 +3264,7 @@ dependencies = [ "editor", "env_logger", "gpui", + "menu", "serde_json", "settings", "theme", @@ -3463,6 +3476,7 @@ dependencies = [ "editor", "futures", "gpui", + "menu", "postage", "project", "serde_json", @@ -4176,6 +4190,7 @@ dependencies = [ "gpui", "language", "log", + "menu", "postage", "project", "serde", diff --git a/crates/chat_panel/Cargo.toml b/crates/chat_panel/Cargo.toml index 95426517d7..e54245502f 100644 --- a/crates/chat_panel/Cargo.toml +++ b/crates/chat_panel/Cargo.toml @@ -11,6 +11,7 @@ doctest = false client = { path = "../client" } editor = { path = "../editor" } gpui = { path = "../gpui" } +menu = { path = "../menu" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 29c64128d1..8bce551a8c 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -11,12 +11,12 @@ use gpui::{ AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, }; +use menu::Confirm; use postage::prelude::Stream; use settings::{Settings, SoftWrap}; use std::sync::Arc; use time::{OffsetDateTime, UtcOffset}; use util::{ResultExt, TryFutureExt}; -use workspace::menu::Confirm; const MESSAGE_LOADING_THRESHOLD: usize = 50; diff --git a/crates/contacts_panel/Cargo.toml b/crates/contacts_panel/Cargo.toml index 800bad497d..ab05a56ce7 100644 --- a/crates/contacts_panel/Cargo.toml +++ b/crates/contacts_panel/Cargo.toml @@ -12,6 +12,7 @@ client = { path = "../client" } editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } +menu = { path = "../menu" } picker = { path = "../picker" } project = { path = "../project" } settings = { path = "../settings" } diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 763772b89e..4b965d3c1d 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -16,15 +16,12 @@ use gpui::{ MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use join_project_notification::JoinProjectNotification; +use menu::{Confirm, SelectNext, SelectPrev}; use serde::Deserialize; use settings::Settings; use std::sync::Arc; use theme::IconButton; -use workspace::{ - menu::{Confirm, SelectNext, SelectPrev}, - sidebar::SidebarItem, - JoinProject, Workspace, -}; +use workspace::{sidebar::SidebarItem, JoinProject, Workspace}; impl_actions!( contacts_panel, diff --git a/crates/context_menu/Cargo.toml b/crates/context_menu/Cargo.toml index 65f7f59a14..817893f43e 100644 --- a/crates/context_menu/Cargo.toml +++ b/crates/context_menu/Cargo.toml @@ -9,6 +9,7 @@ doctest = false [dependencies] gpui = { path = "../gpui" } +menu = { path = "../menu" } settings = { path = "../settings" } theme = { path = "../theme" } smallvec = "1.6" diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index de4f05cade..7e42c2596e 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,18 +1,19 @@ use gpui::{ - elements::*, geometry::vector::Vector2F, impl_internal_actions, platform::CursorStyle, Action, + elements::*, geometry::vector::Vector2F, keymap, platform::CursorStyle, Action, AppContext, Axis, Entity, MutableAppContext, RenderContext, SizeConstraint, View, ViewContext, }; +use menu::*; use settings::Settings; pub fn init(cx: &mut MutableAppContext) { - cx.add_action(ContextMenu::dismiss); + cx.add_action(ContextMenu::select_first); + cx.add_action(ContextMenu::select_last); + cx.add_action(ContextMenu::select_next); + cx.add_action(ContextMenu::select_prev); + cx.add_action(ContextMenu::confirm); + cx.add_action(ContextMenu::cancel); } -#[derive(Clone)] -struct Dismiss; - -impl_internal_actions!(context_menu, [Dismiss]); - pub enum ContextMenuItem { Item { label: String, @@ -32,6 +33,10 @@ impl ContextMenuItem { pub fn separator() -> Self { Self::Separator } + + fn is_separator(&self) -> bool { + matches!(self, Self::Separator) + } } #[derive(Default)] @@ -52,6 +57,12 @@ impl View for ContextMenu { "ContextMenu" } + fn keymap_context(&self, _: &AppContext) -> keymap::Context { + let mut cx = Self::default_keymap_context(); + cx.set.insert("menu".into()); + cx + } + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { if !self.visible { return Empty::new().boxed(); @@ -77,6 +88,7 @@ impl View for ContextMenu { fn on_blur(&mut self, cx: &mut ViewContext) { self.visible = false; + self.selected_index.take(); cx.notify(); } } @@ -86,13 +98,66 @@ impl ContextMenu { Default::default() } - fn dismiss(&mut self, _: &Dismiss, cx: &mut ViewContext) { + fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { + if let Some(ix) = self.selected_index { + if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) { + let window_id = cx.window_id(); + let view_id = cx.view_id(); + cx.dispatch_action_at(window_id, view_id, action.as_ref()); + } + } + } + + fn cancel(&mut self, _: &Cancel, 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()); } } + fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext) { + self.selected_index = self.items.iter().position(|item| !item.is_separator()); + cx.notify(); + } + + fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext) { + for (ix, item) in self.items.iter().enumerate().rev() { + if !item.is_separator() { + self.selected_index = Some(ix); + cx.notify(); + break; + } + } + } + + fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { + if let Some(ix) = self.selected_index { + for (ix, item) in self.items.iter().enumerate().skip(ix + 1) { + if !item.is_separator() { + self.selected_index = Some(ix); + cx.notify(); + break; + } + } + } else { + self.select_first(&Default::default(), cx); + } + } + + fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext) { + if let Some(ix) = self.selected_index { + for (ix, item) in self.items.iter().enumerate().take(ix).rev() { + if !item.is_separator() { + self.selected_index = Some(ix); + cx.notify(); + break; + } + } + } else { + self.select_last(&Default::default(), cx); + } + } + pub fn show( &mut self, position: Vector2F, @@ -202,7 +267,7 @@ impl ContextMenu { .with_cursor_style(CursorStyle::PointingHand) .on_click(move |_, _, cx| { cx.dispatch_any_action(action.boxed_clone()); - cx.dispatch_action(Dismiss); + cx.dispatch_action(Cancel); }) .boxed() } diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index ca3eb6b429..554cf433a2 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -11,6 +11,7 @@ doctest = false editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } +menu = { path = "../menu" } picker = { path = "../picker" } project = { path = "../project" } settings = { path = "../settings" } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index f58c733cc7..84b6973533 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -257,11 +257,9 @@ impl PickerDelegate for FileFinder { mod tests { use super::*; use editor::{Editor, Input}; + use menu::{Confirm, SelectNext}; use serde_json::json; - use workspace::{ - menu::{Confirm, SelectNext}, - AppState, Workspace, - }; + use workspace::{AppState, Workspace}; #[ctor::ctor] fn init_logger() { diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index 76744274c7..93ae96f93e 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -8,9 +8,10 @@ path = "src/go_to_line.rs" doctest = false [dependencies] -text = { path = "../text" } editor = { path = "../editor" } gpui = { path = "../gpui" } +menu = { path = "../menu" } settings = { path = "../settings" } +text = { path = "../text" } workspace = { path = "../workspace" } postage = { version = "0.4", features = ["futures-traits"] } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index bae5ffc46c..f2df235a7b 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -3,12 +3,10 @@ use gpui::{ actions, elements::*, geometry::vector::Vector2F, Axis, Entity, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; +use menu::{Cancel, Confirm}; use settings::Settings; use text::{Bias, Point}; -use workspace::{ - menu::{Cancel, Confirm}, - Workspace, -}; +use workspace::Workspace; actions!(go_to_line, [Toggle]); diff --git a/crates/menu/Cargo.toml b/crates/menu/Cargo.toml new file mode 100644 index 0000000000..cdcacd4416 --- /dev/null +++ b/crates/menu/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "menu" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/menu.rs" +doctest = false + +[dependencies] +gpui = { path = "../gpui" } diff --git a/crates/workspace/src/menu.rs b/crates/menu/src/menu.rs similarity index 100% rename from crates/workspace/src/menu.rs rename to crates/menu/src/menu.rs diff --git a/crates/picker/Cargo.toml b/crates/picker/Cargo.toml index 4528f00687..c74b6927ae 100644 --- a/crates/picker/Cargo.toml +++ b/crates/picker/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] editor = { path = "../editor" } gpui = { path = "../gpui" } +menu = { path = "../menu" } settings = { path = "../settings" } util = { path = "../util" } theme = { path = "../theme" } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 8fd662978b..19dc3054b7 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -10,11 +10,9 @@ use gpui::{ AppContext, Axis, Element, ElementBox, Entity, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; +use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev}; use settings::Settings; use std::cmp; -use workspace::menu::{ - Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev, -}; pub struct Picker { delegate: WeakViewHandle, diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 7eb0282660..6d566699fa 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -11,6 +11,7 @@ doctest = false context_menu = { path = "../context_menu" } editor = { path = "../editor" } gpui = { path = "../gpui" } +menu = { path = "../menu" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 13b27d62dd..5fda6f04fa 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -14,6 +14,7 @@ use gpui::{ AppContext, Element, ElementBox, Entity, ModelHandle, MutableAppContext, PromptLevel, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; +use menu::{Confirm, SelectNext, SelectPrev}; use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use settings::Settings; use std::{ @@ -23,10 +24,7 @@ use std::{ ops::Range, }; use unicase::UniCase; -use workspace::{ - menu::{Confirm, SelectNext, SelectPrev}, - Workspace, -}; +use workspace::Workspace; const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX; diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 56c4fff651..3e80b5979e 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -12,6 +12,7 @@ collections = { path = "../collections" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } +menu = { path = "../menu" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index e3834f6f45..9943ce5ded 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -9,6 +9,7 @@ use gpui::{ ModelHandle, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; +use menu::Confirm; use project::{search::SearchQuery, Project}; use settings::Settings; use smallvec::SmallVec; @@ -19,8 +20,7 @@ use std::{ }; use util::ResultExt as _; use workspace::{ - menu::Confirm, Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, - Workspace, + Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace, }; actions!(project_search, [Deploy, SearchInNew, ToggleFocus]); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6fa4c9a6b2..21361b8081 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,5 +1,4 @@ pub mod lsp_status; -pub mod menu; pub mod pane; pub mod pane_group; pub mod sidebar;