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",
"picker",
"project",
"serde",
"serde_json",
"smol",
"ui",

View File

@ -343,6 +343,13 @@
{
"context": "Workspace",
"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-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",

View File

@ -386,6 +386,13 @@
{
"context": "Workspace",
"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-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",

View File

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

View File

@ -8,12 +8,19 @@ use picker::{
highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText},
Picker, PickerDelegate,
};
use serde::Deserialize;
use std::{path::Path, sync::Arc};
use ui::{prelude::*, tooltip_container, ListItem, ListItemSpacing, Tooltip};
use util::paths::PathExt;
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) {
cx.observe_new_views(RecentProjects::register).detach();
@ -63,9 +70,9 @@ impl RecentProjects {
}
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 {
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);
}
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 {
workspace.update(&mut cx, |workspace, cx| {
let weak_workspace = cx.view().downgrade();
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);
modal
@ -95,7 +107,13 @@ impl RecentProjects {
}
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,
matches: Vec<StringMatch>,
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
reset_selected_match_index: bool,
}
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 {
workspace,
workspaces: vec![],
selected_match_index: 0,
matches: Default::default(),
create_new_window,
render_paths,
reset_selected_match_index: true,
}
@ -148,10 +168,19 @@ impl PickerDelegate for RecentProjectsDelegate {
type ListItem = ListItem;
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
Arc::from(format!(
"{} reuses the window, {} opens a new one",
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!(
"{reuse_window} reuses the window, {create_window} opens a new one",
))
}
@ -220,7 +249,11 @@ impl PickerDelegate for RecentProjectsDelegate {
{
let (candidate_workspace_id, candidate_workspace_location) =
&self.workspaces[selected_match.candidate_id];
let replace_current_window = !secondary;
let replace_current_window = if self.create_new_window {
secondary
} else {
!secondary
};
workspace
.update(cx, |workspace, cx| {
if workspace.database_id() != *candidate_workspace_id {
@ -539,7 +572,12 @@ mod tests {
workspace: &WindowHandle<Workspace>,
cx: &mut TestAppContext,
) -> View<Picker<RecentProjectsDelegate>> {
cx.dispatch_action((*workspace).into(), OpenRecent);
cx.dispatch_action(
(*workspace).into(),
OpenRecent {
create_new_window: false,
},
);
workspace
.update(cx, |workspace, cx| {
workspace

View File

@ -37,7 +37,12 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("New Window", workspace::NewWindow),
MenuItem::separator(),
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::action("Add Folder to Project…", workspace::AddFolderToProject),
MenuItem::action("Save", workspace::Save { save_intent: None }),