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:
parent
37f7957826
commit
3efb871cd4
6 changed files with 72 additions and 13 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue