Cargo.lock 🔗
@@ -15825,6 +15825,7 @@ dependencies = [
"language_model",
"menu",
"project",
+ "recent_projects",
"serde_json",
"settings",
"theme",
Danilo Leal created
This PR makes the "Open Project" button in the sidebar also open the
"Recent Projects" popover, while also anchoring that popover to the the
button on the sidebar instead.
Release Notes:
- N/A
Cargo.lock | 1
crates/sidebar/Cargo.toml | 1
crates/sidebar/src/sidebar.rs | 81 +++++++++++++++++++-------
crates/title_bar/src/title_bar.rs | 67 +++++++++++++++++++++
crates/workspace/src/multi_workspace.rs | 26 ++++++++
5 files changed, 151 insertions(+), 25 deletions(-)
@@ -15825,6 +15825,7 @@ dependencies = [
"language_model",
"menu",
"project",
+ "recent_projects",
"serde_json",
"settings",
"theme",
@@ -26,6 +26,7 @@ fs.workspace = true
gpui.workspace = true
menu.workspace = true
project.workspace = true
+recent_projects.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
@@ -12,20 +12,23 @@ use gpui::{
};
use menu::{Cancel, Confirm, SelectFirst, SelectLast, SelectNext, SelectPrevious};
use project::Event as ProjectEvent;
+use recent_projects::RecentProjects;
use settings::Settings;
use std::collections::{HashMap, HashSet};
use std::mem;
use theme::{ActiveTheme, ThemeSettings};
use ui::utils::TRAFFIC_LIGHT_PADDING;
use ui::{
- AgentThreadStatus, GradientFade, HighlightedLabel, IconButtonShape, KeyBinding, ListItem, Tab,
- ThreadItem, Tooltip, WithScrollbar, prelude::*,
+ AgentThreadStatus, ButtonStyle, GradientFade, HighlightedLabel, IconButtonShape, KeyBinding,
+ ListItem, PopoverMenu, PopoverMenuHandle, Tab, ThreadItem, TintColor, Tooltip, WithScrollbar,
+ prelude::*,
};
use util::path_list::PathList;
use workspace::{
FocusWorkspaceSidebar, MultiWorkspace, MultiWorkspaceEvent, Sidebar as WorkspaceSidebar,
SidebarEvent, ToggleWorkspaceSidebar, Workspace,
};
+use zed_actions::OpenRecent;
use zed_actions::editor::{MoveDown, MoveUp};
actions!(
@@ -183,6 +186,7 @@ pub struct Sidebar {
active_entry_index: Option<usize>,
collapsed_groups: HashSet<PathList>,
expanded_groups: HashMap<PathList, usize>,
+ recent_projects_popover_handle: PopoverMenuHandle<RecentProjects>,
}
impl EventEmitter<SidebarEvent> for Sidebar {}
@@ -278,6 +282,7 @@ impl Sidebar {
active_entry_index: None,
collapsed_groups: HashSet::new(),
expanded_groups: HashMap::new(),
+ recent_projects_popover_handle: PopoverMenuHandle::default(),
}
}
@@ -1174,6 +1179,48 @@ impl Sidebar {
.into_any_element()
}
+ fn render_recent_projects_button(&self, cx: &mut Context<Self>) -> impl IntoElement {
+ let workspace = self
+ .multi_workspace
+ .upgrade()
+ .map(|mw| mw.read(cx).workspace().downgrade());
+
+ let focus_handle = workspace
+ .as_ref()
+ .and_then(|ws| ws.upgrade())
+ .map(|w| w.read(cx).focus_handle(cx))
+ .unwrap_or_else(|| cx.focus_handle());
+
+ let popover_handle = self.recent_projects_popover_handle.clone();
+
+ PopoverMenu::new("sidebar-recent-projects-menu")
+ .with_handle(popover_handle)
+ .menu(move |window, cx| {
+ workspace.as_ref().map(|ws| {
+ RecentProjects::popover(ws.clone(), false, focus_handle.clone(), window, cx)
+ })
+ })
+ .trigger_with_tooltip(
+ IconButton::new("open-project", IconName::OpenFolder)
+ .icon_size(IconSize::Small)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent)),
+ |_window, cx| {
+ Tooltip::for_action(
+ "Recent Projects",
+ &OpenRecent {
+ create_new_window: false,
+ },
+ cx,
+ )
+ },
+ )
+ .anchor(gpui::Corner::TopLeft)
+ .offset(gpui::Point {
+ x: px(0.0),
+ y: px(2.0),
+ })
+ }
+
fn render_filter_input(&self, cx: &mut Context<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
@@ -1315,6 +1362,14 @@ impl WorkspaceSidebar for Sidebar {
fn has_notifications(&self, _cx: &App) -> bool {
!self.contents.notified_threads.is_empty()
}
+
+ fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App) {
+ self.recent_projects_popover_handle.toggle(window, cx);
+ }
+
+ fn is_recent_projects_popover_deployed(&self) -> bool {
+ self.recent_projects_popover_handle.is_deployed()
+ }
}
impl Focusable for Sidebar {
@@ -1412,27 +1467,7 @@ impl Render for Sidebar {
cx.emit(SidebarEvent::Close);
}))
})
- .child(
- IconButton::new("open-project", IconName::OpenFolder)
- .icon_size(IconSize::Small)
- .tooltip(|_window, cx| {
- Tooltip::for_action(
- "Open Project",
- &workspace::Open {
- create_new_window: false,
- },
- cx,
- )
- })
- .on_click(|_event, window, cx| {
- window.dispatch_action(
- Box::new(workspace::Open {
- create_new_window: false,
- }),
- cx,
- );
- }),
- ),
+ .child(self.render_recent_projects_button(cx)),
)
.child(
h_flex()
@@ -151,6 +151,7 @@ pub struct TitleBar {
user_store: Entity<UserStore>,
client: Arc<Client>,
workspace: WeakEntity<Workspace>,
+ multi_workspace: Option<WeakEntity<MultiWorkspace>>,
application_menu: Option<Entity<ApplicationMenu>>,
_subscriptions: Vec<Subscription>,
banner: Entity<OnboardingBanner>,
@@ -188,7 +189,7 @@ impl Render for TitleBar {
.when(title_bar_settings.show_project_items, |title_bar| {
title_bar
.children(self.render_project_host(cx))
- .child(self.render_project_name(cx))
+ .child(self.render_project_name(window, cx))
})
.when(title_bar_settings.show_branch_name, |title_bar| {
title_bar.children(self.render_project_branch(cx))
@@ -389,6 +390,7 @@ impl TitleBar {
if let Some(this) = this.upgrade() {
this.update(cx, |this, _| {
this._subscriptions.push(subscription);
+ this.multi_workspace = Some(multi_workspace.downgrade());
});
}
});
@@ -400,6 +402,7 @@ impl TitleBar {
platform_titlebar,
application_menu,
workspace: workspace.weak_handle(),
+ multi_workspace: None,
project,
user_store,
client,
@@ -718,7 +721,11 @@ impl TitleBar {
)
}
- pub fn render_project_name(&self, cx: &mut Context<Self>) -> impl IntoElement {
+ pub fn render_project_name(
+ &self,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> impl IntoElement {
let workspace = self.workspace.clone();
let name = self.effective_active_worktree(cx).map(|worktree| {
@@ -734,6 +741,19 @@ impl TitleBar {
"Open Recent Project".to_string()
};
+ let is_sidebar_open = self.platform_titlebar.read(cx).is_workspace_sidebar_open();
+
+ if is_sidebar_open {
+ return self
+ .render_project_name_with_sidebar_popover(
+ window,
+ display_name,
+ is_project_selected,
+ cx,
+ )
+ .into_any_element();
+ }
+
let focus_handle = workspace
.upgrade()
.map(|w| w.read(cx).focus_handle(cx))
@@ -773,6 +793,49 @@ impl TitleBar {
.into_any_element()
}
+ fn render_project_name_with_sidebar_popover(
+ &self,
+ _window: &Window,
+ display_name: String,
+ is_project_selected: bool,
+ cx: &mut Context<Self>,
+ ) -> impl IntoElement {
+ let multi_workspace = self.multi_workspace.clone();
+
+ let is_popover_deployed = multi_workspace
+ .as_ref()
+ .and_then(|mw| mw.upgrade())
+ .map(|mw| mw.read(cx).is_recent_projects_popover_deployed(cx))
+ .unwrap_or(false);
+
+ Button::new("project_name_trigger", display_name)
+ .label_size(LabelSize::Small)
+ .when(self.worktree_count(cx) > 1, |this| {
+ this.icon(IconName::ChevronDown)
+ .icon_color(Color::Muted)
+ .icon_size(IconSize::XSmall)
+ })
+ .toggle_state(is_popover_deployed)
+ .selected_style(ButtonStyle::Tinted(TintColor::Accent))
+ .when(!is_project_selected, |s| s.color(Color::Muted))
+ .tooltip(move |_window, cx| {
+ Tooltip::for_action(
+ "Recent Projects",
+ &zed_actions::OpenRecent {
+ create_new_window: false,
+ },
+ cx,
+ )
+ })
+ .on_click(move |_, window, cx| {
+ if let Some(mw) = multi_workspace.as_ref().and_then(|mw| mw.upgrade()) {
+ mw.update(cx, |mw, cx| {
+ mw.toggle_recent_projects_popover(window, cx);
+ });
+ }
+ })
+ }
+
pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
let effective_worktree = self.effective_active_worktree(cx)?;
let repository = self.get_repository_for_worktree(&effective_worktree, cx)?;
@@ -50,6 +50,8 @@ pub trait Sidebar: EventEmitter<SidebarEvent> + Focusable + Render + Sized {
fn width(&self, cx: &App) -> Pixels;
fn set_width(&mut self, width: Option<Pixels>, cx: &mut Context<Self>);
fn has_notifications(&self, cx: &App) -> bool;
+ fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App);
+ fn is_recent_projects_popover_deployed(&self) -> bool;
}
pub trait SidebarHandle: 'static + Send + Sync {
@@ -60,6 +62,8 @@ pub trait SidebarHandle: 'static + Send + Sync {
fn has_notifications(&self, cx: &App) -> bool;
fn to_any(&self) -> AnyView;
fn entity_id(&self) -> EntityId;
+ fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App);
+ fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool;
}
#[derive(Clone)]
@@ -100,6 +104,16 @@ impl<T: Sidebar> SidebarHandle for Entity<T> {
fn entity_id(&self) -> EntityId {
Entity::entity_id(self)
}
+
+ fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App) {
+ self.update(cx, |this, cx| {
+ this.toggle_recent_projects_popover(window, cx);
+ });
+ }
+
+ fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool {
+ self.read(cx).is_recent_projects_popover_deployed()
+ }
}
pub struct MultiWorkspace {
@@ -187,6 +201,18 @@ impl MultiWorkspace {
.map_or(false, |s| s.has_notifications(cx))
}
+ pub fn toggle_recent_projects_popover(&self, window: &mut Window, cx: &mut App) {
+ if let Some(sidebar) = &self.sidebar {
+ sidebar.toggle_recent_projects_popover(window, cx);
+ }
+ }
+
+ pub fn is_recent_projects_popover_deployed(&self, cx: &App) -> bool {
+ self.sidebar
+ .as_ref()
+ .map_or(false, |s| s.is_recent_projects_popover_deployed(cx))
+ }
+
pub fn multi_workspace_enabled(&self, cx: &App) -> bool {
cx.has_flag::<AgentV2FeatureFlag>() && !DisableAiSettings::get_global(cx).disable_ai
}