Add a basic context menu to the project panel
This commit is contained in:
parent
75891e83f3
commit
bcf449d3fe
2 changed files with 98 additions and 75 deletions
|
@ -8,10 +8,11 @@ use file_associations::FileAssociations;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
|
actions, div, overlay, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
|
||||||
ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
|
ClipboardItem, Div, EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement,
|
||||||
Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, Stateful, Styled,
|
Model, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render,
|
||||||
Task, UniformListScrollHandle, View, ViewContext, VisualContext as _, WeakView, WindowContext,
|
Stateful, Styled, Subscription, Task, UniformListScrollHandle, View, ViewContext,
|
||||||
|
VisualContext as _, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::{
|
use project::{
|
||||||
|
@ -29,7 +30,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use theme::ActiveTheme as _;
|
use theme::ActiveTheme as _;
|
||||||
use ui::{v_stack, IconElement, Label, ListItem};
|
use ui::{v_stack, ContextMenu, IconElement, Label, ListItem};
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
use util::{maybe, ResultExt, TryFutureExt};
|
use util::{maybe, ResultExt, TryFutureExt};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
|
@ -49,6 +50,7 @@ pub struct ProjectPanel {
|
||||||
last_worktree_root_id: Option<ProjectEntryId>,
|
last_worktree_root_id: Option<ProjectEntryId>,
|
||||||
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
|
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
|
||||||
selection: Option<Selection>,
|
selection: Option<Selection>,
|
||||||
|
context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||||
edit_state: Option<EditState>,
|
edit_state: Option<EditState>,
|
||||||
filename_editor: View<Editor>,
|
filename_editor: View<Editor>,
|
||||||
clipboard_entry: Option<ClipboardEntry>,
|
clipboard_entry: Option<ClipboardEntry>,
|
||||||
|
@ -231,6 +233,7 @@ impl ProjectPanel {
|
||||||
expanded_dir_ids: Default::default(),
|
expanded_dir_ids: Default::default(),
|
||||||
selection: None,
|
selection: None,
|
||||||
edit_state: None,
|
edit_state: None,
|
||||||
|
context_menu: None,
|
||||||
filename_editor,
|
filename_editor,
|
||||||
clipboard_entry: None,
|
clipboard_entry: None,
|
||||||
// context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
|
// context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
|
||||||
|
@ -366,80 +369,92 @@ impl ProjectPanel {
|
||||||
|
|
||||||
fn deploy_context_menu(
|
fn deploy_context_menu(
|
||||||
&mut self,
|
&mut self,
|
||||||
_position: Point<Pixels>,
|
position: Point<Pixels>,
|
||||||
_entry_id: ProjectEntryId,
|
entry_id: ProjectEntryId,
|
||||||
_cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
// todo!()
|
let this = cx.view().clone();
|
||||||
// let project = self.project.read(cx);
|
let project = self.project.read(cx);
|
||||||
|
|
||||||
// let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) {
|
let worktree_id = if let Some(id) = project.worktree_id_for_entry(entry_id, cx) {
|
||||||
// id
|
id
|
||||||
// } else {
|
} else {
|
||||||
// return;
|
return;
|
||||||
// };
|
};
|
||||||
|
|
||||||
// self.selection = Some(Selection {
|
self.selection = Some(Selection {
|
||||||
// worktree_id,
|
worktree_id,
|
||||||
// entry_id,
|
entry_id,
|
||||||
// });
|
});
|
||||||
|
|
||||||
// let mut menu_entries = Vec::new();
|
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||||
// if let Some((worktree, entry)) = self.selected_entry(cx) {
|
let is_root = Some(entry) == worktree.root_entry();
|
||||||
// let is_root = Some(entry) == worktree.root_entry();
|
let is_dir = entry.is_dir();
|
||||||
// if !project.is_remote() {
|
let worktree_id = worktree.id();
|
||||||
// menu_entries.push(ContextMenuItem::action(
|
let is_local = project.is_local();
|
||||||
// "Add Folder to Project",
|
|
||||||
// workspace::AddFolderToProject,
|
|
||||||
// ));
|
|
||||||
// if is_root {
|
|
||||||
// let project = self.project.clone();
|
|
||||||
// menu_entries.push(ContextMenuItem::handler("Remove from Project", move |cx| {
|
|
||||||
// project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// menu_entries.push(ContextMenuItem::action("New File", NewFile));
|
|
||||||
// menu_entries.push(ContextMenuItem::action("New Folder", NewDirectory));
|
|
||||||
// menu_entries.push(ContextMenuItem::Separator);
|
|
||||||
// menu_entries.push(ContextMenuItem::action("Cut", Cut));
|
|
||||||
// menu_entries.push(ContextMenuItem::action("Copy", Copy));
|
|
||||||
// if let Some(clipboard_entry) = self.clipboard_entry {
|
|
||||||
// if clipboard_entry.worktree_id() == worktree.id() {
|
|
||||||
// menu_entries.push(ContextMenuItem::action("Paste", Paste));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// menu_entries.push(ContextMenuItem::Separator);
|
|
||||||
// menu_entries.push(ContextMenuItem::action("Copy Path", CopyPath));
|
|
||||||
// menu_entries.push(ContextMenuItem::action(
|
|
||||||
// "Copy Relative Path",
|
|
||||||
// CopyRelativePath,
|
|
||||||
// ));
|
|
||||||
|
|
||||||
// if entry.is_dir() {
|
let context_menu = ContextMenu::build(cx, |mut menu, cx| {
|
||||||
// menu_entries.push(ContextMenuItem::Separator);
|
if is_local {
|
||||||
// }
|
menu = menu.action(
|
||||||
// menu_entries.push(ContextMenuItem::action("Reveal in Finder", RevealInFinder));
|
"Add Folder to Project",
|
||||||
// if entry.is_dir() {
|
Box::new(workspace::AddFolderToProject),
|
||||||
// menu_entries.push(ContextMenuItem::action("Open in Terminal", OpenInTerminal));
|
);
|
||||||
// menu_entries.push(ContextMenuItem::action(
|
if is_root {
|
||||||
// "Search Inside",
|
menu = menu.entry(
|
||||||
// NewSearchInDirectory,
|
"Remove from Project",
|
||||||
// ));
|
cx.listener_for(&this, move |this, _, cx| {
|
||||||
// }
|
this.project.update(cx, |project, cx| {
|
||||||
|
project.remove_worktree(worktree_id, cx)
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// menu_entries.push(ContextMenuItem::Separator);
|
menu = menu
|
||||||
// menu_entries.push(ContextMenuItem::action("Rename", Rename));
|
.action("New File", Box::new(NewFile))
|
||||||
// if !is_root {
|
.action("New Folder", Box::new(NewDirectory))
|
||||||
// menu_entries.push(ContextMenuItem::action("Delete", Delete));
|
.separator()
|
||||||
// }
|
.action("Cut", Box::new(Cut))
|
||||||
// }
|
.action("Copy", Box::new(Copy));
|
||||||
|
|
||||||
// // self.context_menu.update(cx, |menu, cx| {
|
if let Some(clipboard_entry) = self.clipboard_entry {
|
||||||
// // menu.show(position, AnchorCorner::TopLeft, menu_entries, cx);
|
if clipboard_entry.worktree_id() == worktree_id {
|
||||||
// // });
|
menu = menu.action("Paste", Box::new(Paste));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cx.notify();
|
menu = menu
|
||||||
|
.separator()
|
||||||
|
.action("Copy Path", Box::new(CopyPath))
|
||||||
|
.action("Copy Relative Path", Box::new(CopyRelativePath))
|
||||||
|
.separator()
|
||||||
|
.action("Reveal in Finder", Box::new(RevealInFinder));
|
||||||
|
|
||||||
|
if is_dir {
|
||||||
|
menu = menu
|
||||||
|
.action("Open in Terminal", Box::new(OpenInTerminal))
|
||||||
|
.action("Search Inside", Box::new(NewSearchInDirectory))
|
||||||
|
}
|
||||||
|
|
||||||
|
menu = menu.separator().action("Rename", Box::new(Rename));
|
||||||
|
|
||||||
|
if !is_root {
|
||||||
|
menu = menu.action("Delete", Box::new(Delete));
|
||||||
|
}
|
||||||
|
|
||||||
|
menu
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.focus_view(&context_menu);
|
||||||
|
let subscription = cx.on_blur(&context_menu.focus_handle(cx), |this, cx| {
|
||||||
|
this.context_menu.take();
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
self.context_menu = Some((context_menu, position, subscription));
|
||||||
|
}
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
|
fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) {
|
||||||
|
@ -1379,6 +1394,9 @@ impl ProjectPanel {
|
||||||
.ml_1(),
|
.ml_1(),
|
||||||
)
|
)
|
||||||
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
|
.on_click(cx.listener(move |this, event: &gpui::ClickEvent, cx| {
|
||||||
|
if event.down.button == MouseButton::Right {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if !show_editor {
|
if !show_editor {
|
||||||
if kind.is_dir() {
|
if kind.is_dir() {
|
||||||
this.toggle_expanded(entry_id, cx);
|
this.toggle_expanded(entry_id, cx);
|
||||||
|
@ -1415,6 +1433,7 @@ impl Render for ProjectPanel {
|
||||||
div()
|
div()
|
||||||
.id("project-panel")
|
.id("project-panel")
|
||||||
.size_full()
|
.size_full()
|
||||||
|
.relative()
|
||||||
.key_context("ProjectPanel")
|
.key_context("ProjectPanel")
|
||||||
.on_action(cx.listener(Self::select_next))
|
.on_action(cx.listener(Self::select_next))
|
||||||
.on_action(cx.listener(Self::select_prev))
|
.on_action(cx.listener(Self::select_prev))
|
||||||
|
@ -1458,6 +1477,12 @@ impl Render for ProjectPanel {
|
||||||
.size_full()
|
.size_full()
|
||||||
.track_scroll(self.list.clone()),
|
.track_scroll(self.list.clone()),
|
||||||
)
|
)
|
||||||
|
.children(self.context_menu.as_ref().map(|(menu, position, _)| {
|
||||||
|
overlay()
|
||||||
|
.position(*position)
|
||||||
|
.anchor(gpui::AnchorCorner::BottomLeft)
|
||||||
|
.child(menu.clone())
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
v_stack()
|
v_stack()
|
||||||
.id("empty-project_panel")
|
.id("empty-project_panel")
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use std::cell::RefCell;
|
use crate::{prelude::*, v_stack, Label, List, ListItem, ListSeparator, ListSubHeader};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crate::{prelude::*, v_stack, Label, List};
|
|
||||||
use crate::{ListItem, ListSeparator, ListSubHeader};
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DismissEvent,
|
overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, ClickEvent, DismissEvent,
|
||||||
DispatchPhase, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId,
|
DispatchPhase, Div, EventEmitter, FocusHandle, FocusableView, IntoElement, LayoutId,
|
||||||
ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
|
ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, VisualContext,
|
||||||
};
|
};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
pub enum ContextMenuItem {
|
pub enum ContextMenuItem {
|
||||||
Separator,
|
Separator,
|
||||||
|
@ -177,6 +174,7 @@ pub struct MenuHandleState<M> {
|
||||||
child_element: Option<AnyElement>,
|
child_element: Option<AnyElement>,
|
||||||
menu_element: Option<AnyElement>,
|
menu_element: Option<AnyElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: ManagedView> Element for MenuHandle<M> {
|
impl<M: ManagedView> Element for MenuHandle<M> {
|
||||||
type State = MenuHandleState<M>;
|
type State = MenuHandleState<M>;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue