From 3efb871cd49d06608dd9c3d7ecc151982229d84d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 2 Mar 2024 00:28:51 +0200 Subject: [PATCH] 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 --- Cargo.lock | 1 + assets/keymaps/default-linux.json | 7 +++ assets/keymaps/default-macos.json | 7 +++ crates/recent_projects/Cargo.toml | 1 + crates/recent_projects/src/recent_projects.rs | 62 +++++++++++++++---- crates/zed/src/app_menus.rs | 7 ++- 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 840d7e2f6c..43584772ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7180,6 +7180,7 @@ dependencies = [ "ordered-float 2.10.0", "picker", "project", + "serde", "serde_json", "smol", "ui", diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index 5f6900713a..865d064884 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -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", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index f5cc4f06a9..957820e586 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -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", diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index eb5638d53e..f96dfbdd7d 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -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 diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 41379a247b..eecd251537 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -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.register_action(|workspace, _: &OpenRecent, cx| { + workspace.register_action(|workspace, open_recent: &OpenRecent, cx| { let Some(recent_projects) = workspace.active_modal::(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) -> Option>> { + fn open( + _: &mut Workspace, + create_new_window: bool, + cx: &mut ViewContext, + ) -> Option>> { 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, cx: &mut WindowContext<'_>) -> View { - 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, 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, render_paths: bool) -> Self { + fn new(workspace: WeakView, 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 { + 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!( - "{} reuses the window, {} opens a new one", - cx.keystroke_text_for(&menu::Confirm), - cx.keystroke_text_for(&menu::SecondaryConfirm), + "{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, cx: &mut TestAppContext, ) -> View> { - cx.dispatch_action((*workspace).into(), OpenRecent); + cx.dispatch_action( + (*workspace).into(), + OpenRecent { + create_new_window: false, + }, + ); workspace .update(cx, |workspace, cx| { workspace diff --git a/crates/zed/src/app_menus.rs b/crates/zed/src/app_menus.rs index 0ea51801ea..43b1d45c15 100644 --- a/crates/zed/src/app_menus.rs +++ b/crates/zed/src/app_menus.rs @@ -37,7 +37,12 @@ pub fn app_menus() -> Vec> { 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 }),