Add a way to change what menu::Confirm does in the recent projects modal (#8688)

Follow-up of
https://github.com/zed-industries/zed/issues/8651#issuecomment-1973411072

Zed current default is still to reuse the current window, but now it's
possible to do
```json
"alt-cmd-o": [
  "projects::OpenRecent",
  {
    "create_new_window": true
  }
]
```
and change this.

menu::Secondary confirm does the action with opposite window creation
strategy.

Release Notes:

- Improved open recent projects flexibility: settings can change whether
`menu::Confirm` opens a new window or reuses the old one
This commit is contained in:
Kirill Bulatov 2024-03-02 00:28:51 +02:00 committed by GitHub
parent 37f7957826
commit 3efb871cd4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 72 additions and 13 deletions

1
Cargo.lock generated
View file

@ -7180,6 +7180,7 @@ dependencies = [
"ordered-float 2.10.0", "ordered-float 2.10.0",
"picker", "picker",
"project", "project",
"serde",
"serde_json", "serde_json",
"smol", "smol",
"ui", "ui",

View file

@ -343,6 +343,13 @@
{ {
"context": "Workspace", "context": "Workspace",
"bindings": { "bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": [
// "projects::OpenRecent",
// {
// "create_new_window": true
// }
// ]
"ctrl-alt-o": "projects::OpenRecent", "ctrl-alt-o": "projects::OpenRecent",
"ctrl-alt-b": "branches::OpenRecent", "ctrl-alt-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal", "ctrl-~": "workspace::NewTerminal",

View file

@ -386,6 +386,13 @@
{ {
"context": "Workspace", "context": "Workspace",
"bindings": { "bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": [
// "projects::OpenRecent",
// {
// "create_new_window": true
// }
// ]
"alt-cmd-o": "projects::OpenRecent", "alt-cmd-o": "projects::OpenRecent",
"alt-cmd-b": "branches::OpenRecent", "alt-cmd-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal", "ctrl-~": "workspace::NewTerminal",

View file

@ -15,6 +15,7 @@ gpui.workspace = true
menu.workspace = true menu.workspace = true
ordered-float.workspace = true ordered-float.workspace = true
picker.workspace = true picker.workspace = true
serde.workspace = true
smol.workspace = true smol.workspace = true
ui.workspace = true ui.workspace = true
util.workspace = true util.workspace = true

View file

@ -8,12 +8,19 @@ use picker::{
highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText}, highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText},
Picker, PickerDelegate, Picker, PickerDelegate,
}; };
use serde::Deserialize;
use std::{path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
use ui::{prelude::*, tooltip_container, ListItem, ListItemSpacing, Tooltip}; use ui::{prelude::*, tooltip_container, ListItem, ListItemSpacing, Tooltip};
use util::paths::PathExt; use util::paths::PathExt;
use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB}; use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB};
gpui::actions!(projects, [OpenRecent]); #[derive(PartialEq, Clone, Deserialize, Default)]
pub struct OpenRecent {
#[serde(default)]
pub create_new_window: bool,
}
gpui::impl_actions!(projects, [OpenRecent]);
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
cx.observe_new_views(RecentProjects::register).detach(); cx.observe_new_views(RecentProjects::register).detach();
@ -63,9 +70,9 @@ impl RecentProjects {
} }
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) { fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
workspace.register_action(|workspace, _: &OpenRecent, cx| { workspace.register_action(|workspace, open_recent: &OpenRecent, cx| {
let Some(recent_projects) = workspace.active_modal::<Self>(cx) else { let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
if let Some(handler) = Self::open(workspace, cx) { if let Some(handler) = Self::open(workspace, open_recent.create_new_window, cx) {
handler.detach_and_log_err(cx); handler.detach_and_log_err(cx);
} }
return; return;
@ -79,12 +86,17 @@ impl RecentProjects {
}); });
} }
fn open(_: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<Task<Result<()>>> { fn open(
_: &mut Workspace,
create_new_window: bool,
cx: &mut ViewContext<Workspace>,
) -> Option<Task<Result<()>>> {
Some(cx.spawn(|workspace, mut cx| async move { Some(cx.spawn(|workspace, mut cx| async move {
workspace.update(&mut cx, |workspace, cx| { workspace.update(&mut cx, |workspace, cx| {
let weak_workspace = cx.view().downgrade(); let weak_workspace = cx.view().downgrade();
workspace.toggle_modal(cx, |cx| { workspace.toggle_modal(cx, |cx| {
let delegate = RecentProjectsDelegate::new(weak_workspace, true); let delegate =
RecentProjectsDelegate::new(weak_workspace, create_new_window, true);
let modal = Self::new(delegate, 34., cx); let modal = Self::new(delegate, 34., cx);
modal modal
@ -95,7 +107,13 @@ impl RecentProjects {
} }
pub fn open_popover(workspace: WeakView<Workspace>, cx: &mut WindowContext<'_>) -> View<Self> { pub fn open_popover(workspace: WeakView<Workspace>, cx: &mut WindowContext<'_>) -> View<Self> {
cx.new_view(|cx| Self::new(RecentProjectsDelegate::new(workspace, false), 20., cx)) cx.new_view(|cx| {
Self::new(
RecentProjectsDelegate::new(workspace, false, false),
20.,
cx,
)
})
} }
} }
@ -127,17 +145,19 @@ pub struct RecentProjectsDelegate {
selected_match_index: usize, selected_match_index: usize,
matches: Vec<StringMatch>, matches: Vec<StringMatch>,
render_paths: bool, render_paths: bool,
create_new_window: bool,
// Flag to reset index when there is a new query vs not reset index when user delete an item // Flag to reset index when there is a new query vs not reset index when user delete an item
reset_selected_match_index: bool, reset_selected_match_index: bool,
} }
impl RecentProjectsDelegate { impl RecentProjectsDelegate {
fn new(workspace: WeakView<Workspace>, render_paths: bool) -> Self { fn new(workspace: WeakView<Workspace>, create_new_window: bool, render_paths: bool) -> Self {
Self { Self {
workspace, workspace,
workspaces: vec![], workspaces: vec![],
selected_match_index: 0, selected_match_index: 0,
matches: Default::default(), matches: Default::default(),
create_new_window,
render_paths, render_paths,
reset_selected_match_index: true, reset_selected_match_index: true,
} }
@ -148,10 +168,19 @@ impl PickerDelegate for RecentProjectsDelegate {
type ListItem = ListItem; type ListItem = ListItem;
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> { fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
let (create_window, reuse_window) = if self.create_new_window {
(
cx.keystroke_text_for(&menu::Confirm),
cx.keystroke_text_for(&menu::SecondaryConfirm),
)
} else {
(
cx.keystroke_text_for(&menu::SecondaryConfirm),
cx.keystroke_text_for(&menu::Confirm),
)
};
Arc::from(format!( Arc::from(format!(
"{} reuses the window, {} opens a new one", "{reuse_window} reuses the window, {create_window} opens a new one",
cx.keystroke_text_for(&menu::Confirm),
cx.keystroke_text_for(&menu::SecondaryConfirm),
)) ))
} }
@ -220,7 +249,11 @@ impl PickerDelegate for RecentProjectsDelegate {
{ {
let (candidate_workspace_id, candidate_workspace_location) = let (candidate_workspace_id, candidate_workspace_location) =
&self.workspaces[selected_match.candidate_id]; &self.workspaces[selected_match.candidate_id];
let replace_current_window = !secondary; let replace_current_window = if self.create_new_window {
secondary
} else {
!secondary
};
workspace workspace
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
if workspace.database_id() != *candidate_workspace_id { if workspace.database_id() != *candidate_workspace_id {
@ -539,7 +572,12 @@ mod tests {
workspace: &WindowHandle<Workspace>, workspace: &WindowHandle<Workspace>,
cx: &mut TestAppContext, cx: &mut TestAppContext,
) -> View<Picker<RecentProjectsDelegate>> { ) -> View<Picker<RecentProjectsDelegate>> {
cx.dispatch_action((*workspace).into(), OpenRecent); cx.dispatch_action(
(*workspace).into(),
OpenRecent {
create_new_window: false,
},
);
workspace workspace
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
workspace workspace

View file

@ -37,7 +37,12 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("New Window", workspace::NewWindow), MenuItem::action("New Window", workspace::NewWindow),
MenuItem::separator(), MenuItem::separator(),
MenuItem::action("Open…", workspace::Open), MenuItem::action("Open…", workspace::Open),
MenuItem::action("Open Recent...", recent_projects::OpenRecent), MenuItem::action(
"Open Recent...",
recent_projects::OpenRecent {
create_new_window: false,
},
),
MenuItem::separator(), MenuItem::separator(),
MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject), MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject),
MenuItem::action("Save", workspace::Save { save_intent: None }), MenuItem::action("Save", workspace::Save { save_intent: None }),