Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 22 additions & 64 deletions crates/sidebar/src/sidebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use util::ResultExt as _;
use util::path_list::{PathList, SerializedPathList};
use workspace::{
AddFolderToProject, CloseWindow, FocusWorkspaceSidebar, MoveWorkspaceToNewWindow,
MultiWorkspace, MultiWorkspaceEvent, NextProjectGroup, NextThread, Open, PreviousProjectGroup,
MultiWorkspace, MultiWorkspaceEvent, NextProject, NextThread, Open, PreviousProject,
PreviousThread, ShowFewerThreads, ShowMoreThreads, Sidebar as WorkspaceSidebar, SidebarSide,
ToggleWorkspaceSidebar, Workspace, WorkspaceId, sidebar_side_context_menu,
};
Expand Down Expand Up @@ -3084,53 +3084,37 @@ impl Sidebar {
Some(mw.workspace().read(cx).project_group_key(cx))
}

fn active_project_header_position(&self, cx: &App) -> Option<usize> {
let active_key = self.active_project_group_key(cx)?;
self.contents
.project_header_indices
.iter()
.position(|&entry_ix| {
matches!(
&self.contents.entries[entry_ix],
ListEntry::ProjectHeader { key, .. } if *key == active_key
)
})
}

fn cycle_project_group_impl(
&mut self,
forward: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
fn cycle_project_impl(&mut self, forward: bool, window: &mut Window, cx: &mut Context<Self>) {
let Some(multi_workspace) = self.multi_workspace.upgrade() else {
return;
};

let header_count = self.contents.project_header_indices.len();
if header_count == 0 {
let keys: Vec<ProjectGroupKey> = multi_workspace
.read(cx)
.project_group_keys()
.cloned()
.collect();
if keys.is_empty() {
return;
}

let current_pos = self.active_project_header_position(cx);
let active_key = self.active_project_group_key(cx);
let current_pos = active_key
.as_ref()
.and_then(|active| keys.iter().position(|k| k == active));

let next_pos = match current_pos {
Some(pos) => {
if forward {
(pos + 1) % header_count
(pos + 1) % keys.len()
} else {
(pos + header_count - 1) % header_count
(pos + keys.len() - 1) % keys.len()
}
}
None => 0,
};

let header_entry_ix = self.contents.project_header_indices[next_pos];
let Some(ListEntry::ProjectHeader { key, .. }) = self.contents.entries.get(header_entry_ix)
else {
return;
};
let path_list = key.path_list().clone();
let path_list = keys[next_pos].path_list().clone();

// Uncollapse the target group so that threads become visible.
self.collapsed_groups.remove(&path_list);
Expand All @@ -3145,22 +3129,17 @@ impl Sidebar {
}
}

fn on_next_project_group(
&mut self,
_: &NextProjectGroup,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.cycle_project_group_impl(true, window, cx);
fn on_next_project(&mut self, _: &NextProject, window: &mut Window, cx: &mut Context<Self>) {
self.cycle_project_impl(true, window, cx);
}

fn on_previous_project_group(
fn on_previous_project(
&mut self,
_: &PreviousProjectGroup,
_: &PreviousProject,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.cycle_project_group_impl(false, window, cx);
self.cycle_project_impl(false, window, cx);
}

fn cycle_thread_impl(&mut self, forward: bool, window: &mut Window, cx: &mut Context<Self>) {
Expand Down Expand Up @@ -3844,27 +3823,6 @@ impl WorkspaceSidebar for Sidebar {
cx.notify();
}

fn toggle_thread_switcher(
&mut self,
select_last: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.toggle_thread_switcher_impl(select_last, window, cx);
}

fn cycle_project_group(&mut self, forward: bool, window: &mut Window, cx: &mut Context<Self>) {
self.cycle_project_group_impl(forward, window, cx);
}

fn cycle_thread(&mut self, forward: bool, window: &mut Window, cx: &mut Context<Self>) {
self.cycle_thread_impl(forward, window, cx);
}

fn move_workspace_to_new_window(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.on_move_workspace_to_new_window(&MoveWorkspaceToNewWindow, window, cx);
}

fn serialized_state(&self, _cx: &App) -> Option<String> {
let serialized = SerializedSidebar {
width: Some(f32::from(self.width)),
Expand Down Expand Up @@ -3960,8 +3918,8 @@ impl Render for Sidebar {
.on_action(cx.listener(Self::toggle_archive))
.on_action(cx.listener(Self::focus_sidebar_filter))
.on_action(cx.listener(Self::on_toggle_thread_switcher))
.on_action(cx.listener(Self::on_next_project_group))
.on_action(cx.listener(Self::on_previous_project_group))
.on_action(cx.listener(Self::on_next_project))
.on_action(cx.listener(Self::on_previous_project))
.on_action(cx.listener(Self::on_next_thread))
.on_action(cx.listener(Self::on_previous_thread))
.on_action(cx.listener(Self::on_show_more_threads))
Expand Down
130 changes: 35 additions & 95 deletions crates/workspace/src/multi_workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use anyhow::Result;
use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt};
use gpui::PathPromptOptions;
use gpui::{
AnyView, App, Context, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
ManagedView, MouseButton, Pixels, Render, Subscription, Task, Tiling, Window, WindowId,
actions, deferred, px,
Action as _, AnyView, App, Context, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle,
Focusable, ManagedView, MouseButton, Pixels, Render, Subscription, Task, Tiling, Window,
WindowId, actions, deferred, px,
};
use project::{DirectoryLister, DisableAiSettings, Project, ProjectGroupKey};
use settings::Settings;
Expand Down Expand Up @@ -40,10 +40,10 @@ actions!(
CloseWorkspaceSidebar,
/// Moves focus to or from the workspace sidebar without closing it.
FocusWorkspaceSidebar,
/// Activates the next project group in the sidebar.
NextProjectGroup,
/// Activates the previous project group in the sidebar.
PreviousProjectGroup,
/// Activates the next project in the sidebar.
NextProject,
/// Activates the previous project in the sidebar.
PreviousProject,
/// Activates the next thread in sidebar order.
NextThread,
/// Activates the previous thread in sidebar order.
Expand Down Expand Up @@ -120,29 +120,6 @@ pub trait Sidebar: Focusable + Render + EventEmitter<SidebarEvent> + Sized {
}
/// Makes focus reset back to the search editor upon toggling the sidebar from outside
fn prepare_for_focus(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {}
/// Opens or cycles the thread switcher popup.
fn toggle_thread_switcher(
&mut self,
_select_last: bool,
_window: &mut Window,
_cx: &mut Context<Self>,
) {
}

/// Activates the next or previous project group.
fn cycle_project_group(
&mut self,
_forward: bool,
_window: &mut Window,
_cx: &mut Context<Self>,
) {
}

/// Activates the next or previous thread in sidebar order.
fn cycle_thread(&mut self, _forward: bool, _window: &mut Window, _cx: &mut Context<Self>) {}

/// Moves the active workspace's project group to a new window.
fn move_workspace_to_new_window(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {}

/// Return an opaque JSON blob of sidebar-specific state to persist.
fn serialized_state(&self, _cx: &App) -> Option<String> {
Expand All @@ -168,11 +145,6 @@ 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_thread_switcher(&self, select_last: bool, window: &mut Window, cx: &mut App);
fn cycle_project_group(&self, forward: bool, window: &mut Window, cx: &mut App);
fn cycle_thread(&self, forward: bool, window: &mut Window, cx: &mut App);
fn move_workspace_to_new_window(&self, window: &mut Window, cx: &mut App);

fn is_threads_list_view_active(&self, cx: &App) -> bool;

fn side(&self, cx: &App) -> SidebarSide;
Expand Down Expand Up @@ -223,42 +195,6 @@ impl<T: Sidebar> SidebarHandle for Entity<T> {
Entity::entity_id(self)
}

fn toggle_thread_switcher(&self, select_last: bool, window: &mut Window, cx: &mut App) {
let entity = self.clone();
window.defer(cx, move |window, cx| {
entity.update(cx, |this, cx| {
this.toggle_thread_switcher(select_last, window, cx);
});
});
}

fn cycle_project_group(&self, forward: bool, window: &mut Window, cx: &mut App) {
let entity = self.clone();
window.defer(cx, move |window, cx| {
entity.update(cx, |this, cx| {
this.cycle_project_group(forward, window, cx);
});
});
}

fn cycle_thread(&self, forward: bool, window: &mut Window, cx: &mut App) {
let entity = self.clone();
window.defer(cx, move |window, cx| {
entity.update(cx, |this, cx| {
this.cycle_thread(forward, window, cx);
});
});
}

fn move_workspace_to_new_window(&self, window: &mut Window, cx: &mut App) {
let entity = self.clone();
window.defer(cx, move |window, cx| {
entity.update(cx, |this, cx| {
this.move_workspace_to_new_window(window, cx);
});
});
}

fn is_threads_list_view_active(&self, cx: &App) -> bool {
self.read(cx).is_threads_list_view_active()
}
Expand Down Expand Up @@ -498,6 +434,20 @@ impl MultiWorkspace {
}
}

fn dispatch_to_sidebar(
&self,
action: Box<dyn gpui::Action>,
window: &mut Window,
cx: &mut App,
) {
if let Some(sidebar) = &self.sidebar {
let focus_handle = sidebar.focus_handle(cx);
window.defer(cx, move |window, cx| {
focus_handle.dispatch_action(&*action, window, cx);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than dispatching to the sidebar, we should do this more like the tab switcher. That way, we keep sidebar related logic in the sidebar, but still can have global actions:

https://github.com/zed-industries/zed/blob/main/crates/tab_switcher/src/tab_switcher.rs#L66

});
}
}

pub fn open_sidebar(&mut self, cx: &mut Context<Self>) {
self.sidebar_open = true;
if let ActiveWorkspace::Transient(workspace) = &self.active_workspace {
Expand Down Expand Up @@ -1508,42 +1458,32 @@ impl Render for MultiWorkspace {
))
.on_action(cx.listener(
|this: &mut Self, action: &ToggleThreadSwitcher, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.toggle_thread_switcher(action.select_last, window, cx);
}
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
},
))
.on_action(
cx.listener(|this: &mut Self, _: &NextProjectGroup, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.cycle_project_group(true, window, cx);
}
cx.listener(|this: &mut Self, action: &NextProject, window, cx| {
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
}),
)
.on_action(cx.listener(
|this: &mut Self, _: &PreviousProjectGroup, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.cycle_project_group(false, window, cx);
}
|this: &mut Self, action: &PreviousProject, window, cx| {
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
},
))
.on_action(cx.listener(|this: &mut Self, _: &NextThread, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.cycle_thread(true, window, cx);
}
}))
.on_action(
cx.listener(|this: &mut Self, _: &PreviousThread, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.cycle_thread(false, window, cx);
}
cx.listener(|this: &mut Self, action: &NextThread, window, cx| {
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
}),
)
.on_action(cx.listener(
|this: &mut Self, _: &MoveWorkspaceToNewWindow, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.move_workspace_to_new_window(window, cx);
}
|this: &mut Self, action: &PreviousThread, window, cx| {
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
},
))
.on_action(cx.listener(
|this: &mut Self, action: &MoveWorkspaceToNewWindow, window, cx| {
this.dispatch_to_sidebar(action.boxed_clone(), window, cx);
},
))
})
Expand Down
7 changes: 3 additions & 4 deletions crates/workspace/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ pub use crate::notifications::NotificationFrame;
pub use dock::Panel;
pub use multi_workspace::{
CloseWorkspaceSidebar, DraggedSidebar, FocusWorkspaceSidebar, MoveWorkspaceToNewWindow,
MultiWorkspace, MultiWorkspaceEvent, NewThread, NextProjectGroup, NextThread,
PreviousProjectGroup, PreviousThread, ShowFewerThreads, ShowMoreThreads, Sidebar, SidebarEvent,
SidebarHandle, SidebarRenderState, SidebarSide, ToggleWorkspaceSidebar,
sidebar_side_context_menu,
MultiWorkspace, MultiWorkspaceEvent, NewThread, NextProject, NextThread, PreviousProject,
PreviousThread, ShowFewerThreads, ShowMoreThreads, Sidebar, SidebarEvent, SidebarHandle,
SidebarRenderState, SidebarSide, ToggleWorkspaceSidebar, sidebar_side_context_menu,
};
pub use path_list::{PathList, SerializedPathList};
pub use toast_layer::{ToastAction, ToastLayer, ToastView};
Expand Down
Loading