Add a way to use splits when opening in file finder (#20507)
This commit is contained in:
parent
90ffd65a10
commit
56cf32cb91
5 changed files with 191 additions and 16 deletions
|
@ -649,7 +649,19 @@
|
|||
},
|
||||
{
|
||||
"context": "FileFinder",
|
||||
"bindings": { "ctrl-shift-p": "file_finder::SelectPrev" }
|
||||
"bindings": {
|
||||
"ctrl-shift-p": "file_finder::SelectPrev",
|
||||
"ctrl-k": "file_finder::OpenMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "FileFinder && menu_open",
|
||||
"bindings": {
|
||||
"u": "pane::SplitUp",
|
||||
"d": "pane::SplitDown",
|
||||
"l": "pane::SplitLeft",
|
||||
"r": "pane::SplitRight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "TabSwitcher",
|
||||
|
|
|
@ -649,7 +649,19 @@
|
|||
},
|
||||
{
|
||||
"context": "FileFinder",
|
||||
"bindings": { "cmd-shift-p": "file_finder::SelectPrev" }
|
||||
"bindings": {
|
||||
"cmd-shift-p": "file_finder::SelectPrev",
|
||||
"cmd-k": "file_finder::OpenMenu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "FileFinder && menu_open",
|
||||
"bindings": {
|
||||
"u": "pane::SplitUp",
|
||||
"d": "pane::SplitDown",
|
||||
"l": "pane::SplitLeft",
|
||||
"r": "pane::SplitRight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "TabSwitcher",
|
||||
|
|
|
@ -15,8 +15,8 @@ use file_icons::FileIcons;
|
|||
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||
use gpui::{
|
||||
actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
||||
FocusableView, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task,
|
||||
View, ViewContext, VisualContext, WeakView,
|
||||
FocusableView, KeyContext, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
|
||||
Styled, Task, View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use new_path_prompt::NewPathPrompt;
|
||||
use open_path_prompt::OpenPathPrompt;
|
||||
|
@ -32,16 +32,30 @@ use std::{
|
|||
},
|
||||
};
|
||||
use text::Point;
|
||||
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
|
||||
use ui::{
|
||||
prelude::*, ButtonLike, ContextMenu, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing,
|
||||
PopoverMenu, PopoverMenuHandle, TintColor,
|
||||
};
|
||||
use util::{paths::PathWithPosition, post_inc, ResultExt};
|
||||
use workspace::{item::PreviewTabsSettings, notifications::NotifyResultExt, ModalView, Workspace};
|
||||
use workspace::{
|
||||
item::PreviewTabsSettings, notifications::NotifyResultExt, pane, ModalView, SplitDirection,
|
||||
Workspace,
|
||||
};
|
||||
|
||||
actions!(file_finder, [SelectPrev]);
|
||||
actions!(file_finder, [SelectPrev, OpenMenu]);
|
||||
|
||||
impl ModalView for FileFinder {}
|
||||
impl ModalView for FileFinder {
|
||||
fn on_before_dismiss(&mut self, cx: &mut ViewContext<Self>) -> workspace::DismissDecision {
|
||||
let submenu_focused = self.picker.update(cx, |picker, cx| {
|
||||
picker.delegate.popover_menu_handle.is_focused(cx)
|
||||
});
|
||||
workspace::DismissDecision::Dismiss(!submenu_focused)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileFinder {
|
||||
picker: View<Picker<FileFinderDelegate>>,
|
||||
picker_focus_handle: FocusHandle,
|
||||
init_modifiers: Option<Modifiers>,
|
||||
}
|
||||
|
||||
|
@ -142,8 +156,14 @@ impl FileFinder {
|
|||
}
|
||||
|
||||
fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
|
||||
let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx));
|
||||
let picker_focus_handle = picker.focus_handle(cx);
|
||||
picker.update(cx, |picker, _| {
|
||||
picker.delegate.focus_handle = picker_focus_handle.clone();
|
||||
});
|
||||
Self {
|
||||
picker: cx.new_view(|cx| Picker::uniform_list(delegate, cx)),
|
||||
picker,
|
||||
picker_focus_handle,
|
||||
init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
|
||||
}
|
||||
}
|
||||
|
@ -168,23 +188,85 @@ impl FileFinder {
|
|||
self.init_modifiers = Some(cx.modifiers());
|
||||
cx.dispatch_action(Box::new(menu::SelectPrev));
|
||||
}
|
||||
|
||||
fn handle_open_menu(&mut self, _: &OpenMenu, cx: &mut ViewContext<Self>) {
|
||||
self.picker.update(cx, |picker, cx| {
|
||||
let menu_handle = &picker.delegate.popover_menu_handle;
|
||||
if !menu_handle.is_deployed() {
|
||||
menu_handle.show(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn go_to_file_split_left(&mut self, _: &pane::SplitLeft, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_file_split_inner(SplitDirection::Left, cx)
|
||||
}
|
||||
|
||||
fn go_to_file_split_right(&mut self, _: &pane::SplitRight, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_file_split_inner(SplitDirection::Right, cx)
|
||||
}
|
||||
|
||||
fn go_to_file_split_up(&mut self, _: &pane::SplitUp, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_file_split_inner(SplitDirection::Up, cx)
|
||||
}
|
||||
|
||||
fn go_to_file_split_down(&mut self, _: &pane::SplitDown, cx: &mut ViewContext<Self>) {
|
||||
self.go_to_file_split_inner(SplitDirection::Down, cx)
|
||||
}
|
||||
|
||||
fn go_to_file_split_inner(
|
||||
&mut self,
|
||||
split_direction: SplitDirection,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.picker.update(cx, |picker, cx| {
|
||||
let delegate = &mut picker.delegate;
|
||||
if let Some(workspace) = delegate.workspace.upgrade() {
|
||||
if let Some(m) = delegate.matches.get(delegate.selected_index()) {
|
||||
let path = match &m {
|
||||
Match::History { path, .. } => {
|
||||
let worktree_id = path.project.worktree_id;
|
||||
ProjectPath {
|
||||
worktree_id,
|
||||
path: Arc::clone(&path.project.path),
|
||||
}
|
||||
}
|
||||
Match::Search(m) => ProjectPath {
|
||||
worktree_id: WorktreeId::from_usize(m.0.worktree_id),
|
||||
path: m.0.path.clone(),
|
||||
},
|
||||
};
|
||||
let open_task = workspace.update(cx, move |workspace, cx| {
|
||||
workspace.split_path_preview(path, false, Some(split_direction), cx)
|
||||
});
|
||||
open_task.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for FileFinder {}
|
||||
|
||||
impl FocusableView for FileFinder {
|
||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||
self.picker.focus_handle(cx)
|
||||
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||
self.picker_focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for FileFinder {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let key_context = self.picker.read(cx).delegate.key_context(cx);
|
||||
v_flex()
|
||||
.key_context("FileFinder")
|
||||
.key_context(key_context)
|
||||
.w(rems(34.))
|
||||
.on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
|
||||
.on_action(cx.listener(Self::handle_select_prev))
|
||||
.on_action(cx.listener(Self::handle_open_menu))
|
||||
.on_action(cx.listener(Self::go_to_file_split_left))
|
||||
.on_action(cx.listener(Self::go_to_file_split_right))
|
||||
.on_action(cx.listener(Self::go_to_file_split_up))
|
||||
.on_action(cx.listener(Self::go_to_file_split_down))
|
||||
.child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +287,8 @@ pub struct FileFinderDelegate {
|
|||
history_items: Vec<FoundPath>,
|
||||
separate_history: bool,
|
||||
first_update: bool,
|
||||
popover_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
/// Use a custom ordering for file finder: the regular one
|
||||
|
@ -533,6 +617,8 @@ impl FileFinderDelegate {
|
|||
history_items,
|
||||
separate_history,
|
||||
first_update: true,
|
||||
popover_menu_handle: PopoverMenuHandle::default(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -845,6 +931,15 @@ impl FileFinderDelegate {
|
|||
|
||||
0
|
||||
}
|
||||
|
||||
fn key_context(&self, cx: &WindowContext) -> KeyContext {
|
||||
let mut key_context = KeyContext::new_with_defaults();
|
||||
key_context.add("FileFinder");
|
||||
if self.popover_menu_handle.is_focused(cx) {
|
||||
key_context.add("menu_open");
|
||||
}
|
||||
key_context
|
||||
}
|
||||
}
|
||||
|
||||
impl PickerDelegate for FileFinderDelegate {
|
||||
|
@ -958,7 +1053,7 @@ impl PickerDelegate for FileFinderDelegate {
|
|||
let allow_preview =
|
||||
PreviewTabsSettings::get_global(cx).enable_preview_from_file_finder;
|
||||
if secondary {
|
||||
workspace.split_path_preview(project_path, allow_preview, cx)
|
||||
workspace.split_path_preview(project_path, allow_preview, None, cx)
|
||||
} else {
|
||||
workspace.open_path_preview(
|
||||
project_path,
|
||||
|
@ -1125,6 +1220,60 @@ impl PickerDelegate for FileFinderDelegate {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
|
||||
let menu_open = self.popover_menu_handle.is_focused(cx);
|
||||
Some(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.border_t_1()
|
||||
.py_2()
|
||||
.pr_2()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.justify_end()
|
||||
.child(
|
||||
ButtonLike::new("open-selection")
|
||||
.when_some(KeyBinding::for_action(&menu::Confirm, cx), |button, key| {
|
||||
button.child(key)
|
||||
})
|
||||
.child(Label::new("Open"))
|
||||
.on_click(|_, cx| cx.dispatch_action(menu::Confirm.boxed_clone())),
|
||||
)
|
||||
.child(
|
||||
div().pl_2().child(
|
||||
PopoverMenu::new("menu-popover")
|
||||
.with_handle(self.popover_menu_handle.clone())
|
||||
.attach(gpui::AnchorCorner::TopRight)
|
||||
.anchor(gpui::AnchorCorner::BottomRight)
|
||||
.trigger(
|
||||
ButtonLike::new("menu-trigger")
|
||||
.selected(menu_open)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
.when_some(
|
||||
KeyBinding::for_action_in(
|
||||
&OpenMenu,
|
||||
&self.focus_handle,
|
||||
cx,
|
||||
),
|
||||
|button, key| button.child(key),
|
||||
)
|
||||
.child(Label::new("More actions")),
|
||||
)
|
||||
.menu({
|
||||
move |cx| {
|
||||
Some(ContextMenu::build(cx, move |menu, _| {
|
||||
menu.action("Split left", pane::SplitLeft.boxed_clone())
|
||||
.action("Split right", pane::SplitRight.boxed_clone())
|
||||
.action("Split up", pane::SplitUp.boxed_clone())
|
||||
.action("Split down", pane::SplitDown.boxed_clone())
|
||||
}))
|
||||
}
|
||||
}),
|
||||
),
|
||||
)
|
||||
.into_any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -66,7 +66,7 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
|
|||
.map_or(false, |state| state.menu.borrow().as_ref().is_some())
|
||||
}
|
||||
|
||||
pub fn is_focused(&self, cx: &mut WindowContext) -> bool {
|
||||
pub fn is_focused(&self, cx: &WindowContext) -> bool {
|
||||
self.0.borrow().as_ref().map_or(false, |state| {
|
||||
state
|
||||
.menu
|
||||
|
|
|
@ -2631,13 +2631,14 @@ impl Workspace {
|
|||
path: impl Into<ProjectPath>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
|
||||
self.split_path_preview(path, false, cx)
|
||||
self.split_path_preview(path, false, None, cx)
|
||||
}
|
||||
|
||||
pub fn split_path_preview(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
allow_preview: bool,
|
||||
split_direction: Option<SplitDirection>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
|
||||
let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
|
||||
|
@ -2658,7 +2659,8 @@ impl Workspace {
|
|||
let (project_entry_id, build_item) = task.await?;
|
||||
this.update(&mut cx, move |this, cx| -> Option<_> {
|
||||
let pane = pane.upgrade()?;
|
||||
let new_pane = this.split_pane(pane, SplitDirection::Right, cx);
|
||||
let new_pane =
|
||||
this.split_pane(pane, split_direction.unwrap_or(SplitDirection::Right), cx);
|
||||
new_pane.update(cx, |new_pane, cx| {
|
||||
Some(new_pane.open_item(project_entry_id, true, allow_preview, cx, build_item))
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue