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",
|
"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",
|
"context": "TabSwitcher",
|
||||||
|
|
|
@ -649,7 +649,19 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "FileFinder",
|
"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",
|
"context": "TabSwitcher",
|
||||||
|
|
|
@ -15,8 +15,8 @@ use file_icons::FileIcons;
|
||||||
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
actions, rems, Action, AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle,
|
||||||
FocusableView, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task,
|
FocusableView, KeyContext, Model, Modifiers, ModifiersChangedEvent, ParentElement, Render,
|
||||||
View, ViewContext, VisualContext, WeakView,
|
Styled, Task, View, ViewContext, VisualContext, WeakView,
|
||||||
};
|
};
|
||||||
use new_path_prompt::NewPathPrompt;
|
use new_path_prompt::NewPathPrompt;
|
||||||
use open_path_prompt::OpenPathPrompt;
|
use open_path_prompt::OpenPathPrompt;
|
||||||
|
@ -32,16 +32,30 @@ use std::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use text::Point;
|
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 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 {
|
pub struct FileFinder {
|
||||||
picker: View<Picker<FileFinderDelegate>>,
|
picker: View<Picker<FileFinderDelegate>>,
|
||||||
|
picker_focus_handle: FocusHandle,
|
||||||
init_modifiers: Option<Modifiers>,
|
init_modifiers: Option<Modifiers>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +156,14 @@ impl FileFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
|
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 {
|
Self {
|
||||||
picker: cx.new_view(|cx| Picker::uniform_list(delegate, cx)),
|
picker,
|
||||||
|
picker_focus_handle,
|
||||||
init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
|
init_modifiers: cx.modifiers().modified().then_some(cx.modifiers()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,23 +188,85 @@ impl FileFinder {
|
||||||
self.init_modifiers = Some(cx.modifiers());
|
self.init_modifiers = Some(cx.modifiers());
|
||||||
cx.dispatch_action(Box::new(menu::SelectPrev));
|
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 EventEmitter<DismissEvent> for FileFinder {}
|
||||||
|
|
||||||
impl FocusableView for FileFinder {
|
impl FocusableView for FileFinder {
|
||||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
|
||||||
self.picker.focus_handle(cx)
|
self.picker_focus_handle.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for FileFinder {
|
impl Render for FileFinder {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let key_context = self.picker.read(cx).delegate.key_context(cx);
|
||||||
v_flex()
|
v_flex()
|
||||||
.key_context("FileFinder")
|
.key_context(key_context)
|
||||||
.w(rems(34.))
|
.w(rems(34.))
|
||||||
.on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
|
.on_modifiers_changed(cx.listener(Self::handle_modifiers_changed))
|
||||||
.on_action(cx.listener(Self::handle_select_prev))
|
.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())
|
.child(self.picker.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +287,8 @@ pub struct FileFinderDelegate {
|
||||||
history_items: Vec<FoundPath>,
|
history_items: Vec<FoundPath>,
|
||||||
separate_history: bool,
|
separate_history: bool,
|
||||||
first_update: bool,
|
first_update: bool,
|
||||||
|
popover_menu_handle: PopoverMenuHandle<ContextMenu>,
|
||||||
|
focus_handle: FocusHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use a custom ordering for file finder: the regular one
|
/// Use a custom ordering for file finder: the regular one
|
||||||
|
@ -533,6 +617,8 @@ impl FileFinderDelegate {
|
||||||
history_items,
|
history_items,
|
||||||
separate_history,
|
separate_history,
|
||||||
first_update: true,
|
first_update: true,
|
||||||
|
popover_menu_handle: PopoverMenuHandle::default(),
|
||||||
|
focus_handle: cx.focus_handle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,6 +931,15 @@ impl FileFinderDelegate {
|
||||||
|
|
||||||
0
|
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 {
|
impl PickerDelegate for FileFinderDelegate {
|
||||||
|
@ -958,7 +1053,7 @@ impl PickerDelegate for FileFinderDelegate {
|
||||||
let allow_preview =
|
let allow_preview =
|
||||||
PreviewTabsSettings::get_global(cx).enable_preview_from_file_finder;
|
PreviewTabsSettings::get_global(cx).enable_preview_from_file_finder;
|
||||||
if secondary {
|
if secondary {
|
||||||
workspace.split_path_preview(project_path, allow_preview, cx)
|
workspace.split_path_preview(project_path, allow_preview, None, cx)
|
||||||
} else {
|
} else {
|
||||||
workspace.open_path_preview(
|
workspace.open_path_preview(
|
||||||
project_path,
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl<M: ManagedView> PopoverMenuHandle<M> {
|
||||||
.map_or(false, |state| state.menu.borrow().as_ref().is_some())
|
.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| {
|
self.0.borrow().as_ref().map_or(false, |state| {
|
||||||
state
|
state
|
||||||
.menu
|
.menu
|
||||||
|
|
|
@ -2631,13 +2631,14 @@ impl Workspace {
|
||||||
path: impl Into<ProjectPath>,
|
path: impl Into<ProjectPath>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
|
) -> 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(
|
pub fn split_path_preview(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: impl Into<ProjectPath>,
|
path: impl Into<ProjectPath>,
|
||||||
allow_preview: bool,
|
allow_preview: bool,
|
||||||
|
split_direction: Option<SplitDirection>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
|
) -> Task<Result<Box<dyn ItemHandle>, anyhow::Error>> {
|
||||||
let pane = self.last_active_center_pane.clone().unwrap_or_else(|| {
|
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?;
|
let (project_entry_id, build_item) = task.await?;
|
||||||
this.update(&mut cx, move |this, cx| -> Option<_> {
|
this.update(&mut cx, move |this, cx| -> Option<_> {
|
||||||
let pane = pane.upgrade()?;
|
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| {
|
new_pane.update(cx, |new_pane, cx| {
|
||||||
Some(new_pane.open_item(project_entry_id, true, allow_preview, cx, build_item))
|
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