sidebar: Close all button (#51755)

Cameron Mcloughlin created

Change summary

crates/sidebar/src/sidebar.rs | 64 +++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)

Detailed changes

crates/sidebar/src/sidebar.rs 🔗

@@ -1314,6 +1314,44 @@ impl Sidebar {
         });
     }
 
+    fn close_all_projects(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        let Some(multi_workspace) = self.multi_workspace.upgrade() else {
+            return;
+        };
+
+        let workspace_count = multi_workspace.read(cx).workspaces().len();
+        let active_index = multi_workspace.read(cx).active_workspace_index();
+
+        // Remove all workspaces except the active one, iterating in reverse
+        // so that indices of not-yet-visited workspaces remain valid.
+        for index in (0..workspace_count).rev() {
+            if index != active_index {
+                multi_workspace.update(cx, |multi_workspace, cx| {
+                    multi_workspace.remove_workspace(index, window, cx);
+                });
+            }
+        }
+
+        // Remove all worktrees from the remaining workspace so it becomes empty.
+        let workspace = multi_workspace.read(cx).workspace().clone();
+        let worktree_ids: Vec<_> = workspace
+            .read(cx)
+            .project()
+            .read(cx)
+            .visible_worktrees(cx)
+            .map(|worktree| worktree.read(cx).id())
+            .collect();
+
+        workspace.update(cx, |workspace, cx| {
+            let project = workspace.project().clone();
+            project.update(cx, |project, cx| {
+                for worktree_id in worktree_ids {
+                    project.remove_worktree(worktree_id, cx);
+                }
+            });
+        });
+    }
+
     fn toggle_collapse(
         &mut self,
         path_list: &PathList,
@@ -1913,6 +1951,22 @@ impl Sidebar {
     ) -> impl IntoElement {
         let has_query = self.has_filter_query(cx);
         let needs_traffic_light_padding = cfg!(target_os = "macos") && !window.is_fullscreen();
+        let has_open_projects = self
+            .multi_workspace
+            .upgrade()
+            .map(|mw| {
+                let mw = mw.read(cx);
+                mw.workspaces().len() > 1
+                    || mw
+                        .workspace()
+                        .read(cx)
+                        .project()
+                        .read(cx)
+                        .visible_worktrees(cx)
+                        .next()
+                        .is_some()
+            })
+            .unwrap_or(false);
 
         v_flex()
             .flex_none()
@@ -1948,6 +2002,16 @@ impl Sidebar {
                                         })),
                                 )
                             })
+                            .when(has_open_projects, |this| {
+                                this.child(
+                                    IconButton::new("close-all-projects", IconName::Exit)
+                                        .icon_size(IconSize::Small)
+                                        .tooltip(Tooltip::text("Close All Projects"))
+                                        .on_click(cx.listener(|this, _, window, cx| {
+                                            this.close_all_projects(window, cx);
+                                        })),
+                                )
+                            })
                             .child(
                                 IconButton::new("archive", IconName::Archive)
                                     .icon_size(IconSize::Small)