Adjust "recent projects" modal behavior to allow opening projects in both current and new window (#8267)

![image](https://github.com/zed-industries/zed/assets/2690773/7a0927e8-f32a-4502-8a8a-c7f8e5f325bb)

Fixes https://github.com/zed-industries/zed/issues/7419 by changing the
way "recent projects" modal confirm actions work:
* `menu::Confirm` now reuses the current window when opening a recent
project
* `menu::SecondaryConfirm` now opens a recent project in the new window 
* neither confirm tries to open the current project anymore
* modal's placeholder is adjusted to emphasize this behavior

Release Notes:

- Added a way to open recent projects in the new window
This commit is contained in:
Kirill Bulatov 2024-02-23 13:17:31 +02:00 committed by GitHub
parent a588f674db
commit 71557f3eb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 30 additions and 10 deletions

1
Cargo.lock generated
View file

@ -7343,6 +7343,7 @@ dependencies = [
"fuzzy", "fuzzy",
"gpui", "gpui",
"language", "language",
"menu",
"ordered-float 2.10.0", "ordered-float 2.10.0",
"picker", "picker",
"postage", "postage",

View file

@ -15,6 +15,7 @@ futures.workspace = true
fuzzy.workspace = true fuzzy.workspace = true
gpui.workspace = true gpui.workspace = true
language.workspace = true language.workspace = true
menu.workspace = true
ordered-float.workspace = true ordered-float.workspace = true
picker.workspace = true picker.workspace = true
postage.workspace = true postage.workspace = true

View file

@ -1 +0,0 @@
gpui::actions!(projects, [OpenRecent]);

View file

@ -1,5 +1,4 @@
mod highlighted_workspace_location; mod highlighted_workspace_location;
mod projects;
use fuzzy::{StringMatch, StringMatchCandidate}; use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{ use gpui::{
@ -14,7 +13,7 @@ use ui::{prelude::*, tooltip_container, HighlightedLabel, ListItem, ListItemSpac
use util::paths::PathExt; use util::paths::PathExt;
use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB}; use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB};
pub use projects::OpenRecent; gpui::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();
@ -94,6 +93,7 @@ impl RecentProjects {
Ok(()) Ok(())
})) }))
} }
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), 20., cx))
} }
@ -147,7 +147,11 @@ impl PickerDelegate for RecentProjectsDelegate {
type ListItem = ListItem; type ListItem = ListItem;
fn placeholder_text(&self) -> Arc<str> { fn placeholder_text(&self) -> Arc<str> {
"Search recent projects...".into() Arc::from(format!(
"`{:?}` reuses the window, `{:?}` opens in new",
menu::Confirm,
menu::SecondaryConfirm,
))
} }
fn match_count(&self) -> usize { fn match_count(&self) -> usize {
@ -207,17 +211,26 @@ impl PickerDelegate for RecentProjectsDelegate {
Task::ready(()) Task::ready(())
} }
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) { fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>) {
if let Some((selected_match, workspace)) = self if let Some((selected_match, workspace)) = self
.matches .matches
.get(self.selected_index()) .get(self.selected_index())
.zip(self.workspace.upgrade()) .zip(self.workspace.upgrade())
{ {
let (_, workspace_location) = &self.workspaces[selected_match.candidate_id]; let (candidate_workspace_id, candidate_workspace_location) =
&self.workspaces[selected_match.candidate_id];
let replace_current_window = !secondary;
workspace workspace
.update(cx, |workspace, cx| { .update(cx, |workspace, cx| {
workspace if workspace.database_id() != *candidate_workspace_id {
.open_workspace_for_paths(workspace_location.paths().as_ref().clone(), cx) workspace.open_workspace_for_paths(
replace_current_window,
candidate_workspace_location.paths().as_ref().clone(),
cx,
)
} else {
Task::ready(Ok(()))
}
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
cx.emit(DismissEvent); cx.emit(DismissEvent);

View file

@ -1387,7 +1387,9 @@ impl Workspace {
}; };
if let Some(task) = this if let Some(task) = this
.update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx)) .update(&mut cx, |this, cx| {
this.open_workspace_for_paths(false, paths, cx)
})
.log_err() .log_err()
{ {
task.await.log_err(); task.await.log_err();
@ -1398,6 +1400,7 @@ impl Workspace {
pub fn open_workspace_for_paths( pub fn open_workspace_for_paths(
&mut self, &mut self,
replace_current_window: bool,
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
@ -1405,7 +1408,10 @@ impl Workspace {
let is_remote = self.project.read(cx).is_remote(); let is_remote = self.project.read(cx).is_remote();
let has_worktree = self.project.read(cx).worktrees().next().is_some(); let has_worktree = self.project.read(cx).worktrees().next().is_some();
let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
let window_to_replace = if is_remote || has_worktree || has_dirty_items {
let window_to_replace = if replace_current_window {
window
} else if is_remote || has_worktree || has_dirty_items {
None None
} else { } else {
window window