Use cmd-b/cmd-r/cmd-j to toggle left/right/bottom dock and focus when opening

Antonio Scandurra and Nathan Sobo created

Also, bind the same keys with shift to toggle the dock without focusing.

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

assets/keymaps/default.json       | 25 +++++++++++++
crates/welcome/src/welcome.rs     |  2 
crates/workspace/src/dock.rs      | 10 +++++
crates/workspace/src/workspace.rs | 57 +++++++++++++++++++++++---------
crates/zed/src/menus.rs           | 15 ++++++-
crates/zed/src/zed.rs             |  2 
6 files changed, 89 insertions(+), 22 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -367,7 +367,30 @@
         "workspace::ActivatePane",
         8
       ],
-      "cmd-b": "workspace::ToggleLeftDock",
+      "cmd-b": [
+        "workspace::ToggleLeftDock",
+        { "focus": true }
+      ],
+      "cmd-shift-b": [
+        "workspace::ToggleLeftDock",
+        { "focus": false }
+      ],
+      "cmd-r": [
+        "workspace::ToggleRightDock",
+        { "focus": true }
+      ],
+      "cmd-shift-r": [
+        "workspace::ToggleRightDock",
+        { "focus": false }
+      ],
+      "cmd-j": [
+        "workspace::ToggleBottomDock",
+        { "focus": true }
+      ],
+      "cmd-shift-j": [
+        "workspace::ToggleBottomDock",
+        { "focus": false }
+      ],
       "cmd-shift-f": "workspace::NewSearch",
       "cmd-k cmd-t": "theme_selector::Toggle",
       "cmd-k cmd-s": "zed::OpenKeymap",

crates/welcome/src/welcome.rs 🔗

@@ -32,7 +32,7 @@ pub fn init(cx: &mut AppContext) {
 
 pub fn show_welcome_experience(app_state: &Arc<AppState>, cx: &mut AppContext) {
     open_new(&app_state, cx, |workspace, cx| {
-        workspace.toggle_dock(DockPosition::Left, cx);
+        workspace.toggle_dock(DockPosition::Left, false, cx);
         let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx));
         workspace.add_item_to_center(Box::new(welcome_page.clone()), cx);
         cx.focus(&welcome_page);

crates/workspace/src/dock.rs 🔗

@@ -423,6 +423,16 @@ impl View for Dock {
             Empty::new().into_any()
         }
     }
+
+    fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+        if cx.is_self_focused() {
+            if let Some(active_entry) = self.active_entry() {
+                cx.focus(active_entry.panel.as_any());
+            } else {
+                cx.focus_parent();
+            }
+        }
+    }
 }
 
 impl PanelButtons {

crates/workspace/src/workspace.rs 🔗

@@ -103,6 +103,21 @@ pub trait Modal: View {
 #[derive(Clone, PartialEq)]
 pub struct RemoveWorktreeFromProject(pub WorktreeId);
 
+#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
+pub struct ToggleLeftDock {
+    pub focus: bool,
+}
+
+#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
+pub struct ToggleBottomDock {
+    pub focus: bool,
+}
+
+#[derive(Copy, Clone, Default, Deserialize, PartialEq)]
+pub struct ToggleRightDock {
+    pub focus: bool,
+}
+
 actions!(
     workspace,
     [
@@ -118,9 +133,6 @@ actions!(
         ActivatePreviousPane,
         ActivateNextPane,
         FollowNextCollaborator,
-        ToggleLeftDock,
-        ToggleRightDock,
-        ToggleBottomDock,
         NewTerminal,
         ToggleTerminalFocus,
         NewSearch,
@@ -133,6 +145,11 @@ actions!(
 
 actions!(zed, [OpenSettings]);
 
+impl_actions!(
+    workspace,
+    [ToggleLeftDock, ToggleBottomDock, ToggleRightDock]
+);
+
 #[derive(Clone, PartialEq)]
 pub struct OpenPaths {
     pub paths: Vec<PathBuf>,
@@ -249,14 +266,14 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
     cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| {
         workspace.activate_next_pane(cx)
     });
-    cx.add_action(|workspace: &mut Workspace, _: &ToggleLeftDock, cx| {
-        workspace.toggle_dock(DockPosition::Left, cx);
+    cx.add_action(|workspace: &mut Workspace, action: &ToggleLeftDock, cx| {
+        workspace.toggle_dock(DockPosition::Left, action.focus, cx);
     });
-    cx.add_action(|workspace: &mut Workspace, _: &ToggleRightDock, cx| {
-        workspace.toggle_dock(DockPosition::Right, cx);
+    cx.add_action(|workspace: &mut Workspace, action: &ToggleRightDock, cx| {
+        workspace.toggle_dock(DockPosition::Right, action.focus, cx);
     });
-    cx.add_action(|workspace: &mut Workspace, _: &ToggleBottomDock, cx| {
-        workspace.toggle_dock(DockPosition::Bottom, cx);
+    cx.add_action(|workspace: &mut Workspace, action: &ToggleBottomDock, cx| {
+        workspace.toggle_dock(DockPosition::Bottom, action.focus, cx);
     });
     cx.add_action(Workspace::activate_pane_at_index);
 
@@ -1455,21 +1472,29 @@ impl Workspace {
         }
     }
 
-    pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext<Self>) {
+    pub fn toggle_dock(
+        &mut self,
+        dock_side: DockPosition,
+        focus: bool,
+        cx: &mut ViewContext<Self>,
+    ) {
         let dock = match dock_side {
-            DockPosition::Left => &mut self.left_dock,
-            DockPosition::Bottom => &mut self.bottom_dock,
-            DockPosition::Right => &mut self.right_dock,
+            DockPosition::Left => &self.left_dock,
+            DockPosition::Bottom => &self.bottom_dock,
+            DockPosition::Right => &self.right_dock,
         };
         dock.update(cx, |dock, cx| {
             let open = !dock.is_open();
             dock.set_open(open, cx);
         });
 
-        self.serialize_workspace(cx);
-
-        cx.focus_self();
+        if dock.read(cx).is_open() && focus {
+            cx.focus(dock);
+        } else {
+            cx.focus_self();
+        }
         cx.notify();
+        self.serialize_workspace(cx);
     }
 
     pub fn toggle_panel(&mut self, action: &TogglePanel, cx: &mut ViewContext<Self>) {

crates/zed/src/menus.rs 🔗

@@ -89,9 +89,18 @@ pub fn menus() -> Vec<Menu<'static>> {
                 MenuItem::action("Zoom Out", super::DecreaseBufferFontSize),
                 MenuItem::action("Reset Zoom", super::ResetBufferFontSize),
                 MenuItem::separator(),
-                MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
-                MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
-                MenuItem::action("Toggle Bottom Dock", workspace::ToggleBottomDock),
+                MenuItem::action(
+                    "Toggle Left Dock",
+                    workspace::ToggleLeftDock { focus: false },
+                ),
+                MenuItem::action(
+                    "Toggle Right Dock",
+                    workspace::ToggleRightDock { focus: false },
+                ),
+                MenuItem::action(
+                    "Toggle Bottom Dock",
+                    workspace::ToggleBottomDock { focus: false },
+                ),
                 MenuItem::submenu(Menu {
                     name: "Editor Layout",
                     items: vec![

crates/zed/src/zed.rs 🔗

@@ -354,7 +354,7 @@ pub fn initialize_workspace(
                             .map_or(false, |entry| entry.is_dir())
                     })
             {
-                workspace.toggle_dock(project_panel_position, cx);
+                workspace.toggle_dock(project_panel_position, false, cx);
             }
 
             workspace.add_panel(terminal_panel, cx)