Rebind `ctrl-`` to toggle terminal panel focus

Antonio Scandurra and Mikayla Maki created

Also, add `ctrl-~` to create new terminals.

Co-Authored-By: Mikayla Maki <mikayla@zed.dev>

Change summary

assets/keymaps/default.json                |  3 +
crates/terminal_view/src/terminal_panel.rs |  6 ++-
crates/workspace/src/dock.rs               |  6 +++
crates/workspace/src/workspace.rs          | 45 ++++++++++-------------
crates/zed/src/zed.rs                      | 16 +++++---
5 files changed, 42 insertions(+), 34 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -39,7 +39,8 @@
             "cmd-shift-n": "workspace::NewWindow",
             "cmd-o": "workspace::Open",
             "alt-cmd-o": "projects::OpenRecent",
-            "ctrl-`": "workspace::NewTerminal"
+            "ctrl-~": "workspace::NewTerminal",
+            "ctrl-`": "terminal_panel::ToggleFocus"
         }
     },
     {

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -1,7 +1,7 @@
 use crate::TerminalView;
 use gpui::{
-    elements::*, AppContext, Entity, ModelHandle, Subscription, View, ViewContext, ViewHandle,
-    WeakViewHandle, WindowContext,
+    actions, elements::*, AppContext, Entity, ModelHandle, Subscription, View, ViewContext,
+    ViewHandle, WeakViewHandle, WindowContext,
 };
 use project::Project;
 use settings::{settings_file::SettingsFile, Settings, TerminalDockPosition, WorkingDirectory};
@@ -11,6 +11,8 @@ use workspace::{
     pane, DraggedItem, Pane, Workspace,
 };
 
+actions!(terminal_panel, [ToggleFocus]);
+
 pub fn init(cx: &mut AppContext) {
     cx.add_action(TerminalPanel::add_terminal);
 }

crates/workspace/src/dock.rs 🔗

@@ -176,6 +176,12 @@ impl Dock {
             .map_or(false, |panel| panel.has_focus(cx))
     }
 
+    pub fn panel_index<T: Panel>(&self) -> Option<usize> {
+        self.panel_entries
+            .iter()
+            .position(|entry| entry.panel.as_any().is::<T>())
+    }
+
     pub fn active_panel_index(&self) -> usize {
         self.active_panel_index
     }

crates/workspace/src/workspace.rs 🔗

@@ -116,6 +116,7 @@ actions!(
         FollowNextCollaborator,
         ToggleLeftDock,
         NewTerminal,
+        ToggleTerminalFocus,
         NewSearch,
         Feedback,
         Restart,
@@ -1475,33 +1476,27 @@ impl Workspace {
         cx.notify();
     }
 
-    pub fn toggle_panel_focus(
-        &mut self,
-        dock_position: DockPosition,
-        panel_index: usize,
-        cx: &mut ViewContext<Self>,
-    ) {
-        let dock = match dock_position {
-            DockPosition::Left => &mut self.left_dock,
-            DockPosition::Bottom => &mut self.bottom_dock,
-            DockPosition::Right => &mut self.right_dock,
-        };
-        let active_item = dock.update(cx, |dock, cx| {
-            dock.set_open(true, cx);
-            dock.activate_panel(panel_index, cx);
-            dock.active_panel().cloned()
-        });
-        if let Some(active_item) = active_item {
-            if active_item.has_focus(cx) {
-                cx.focus_self();
-            } else {
-                cx.focus(active_item.as_any());
+    pub fn toggle_panel_focus<T: Panel>(&mut self, cx: &mut ViewContext<Self>) {
+        for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
+            if let Some(panel_index) = dock.read(cx).panel_index::<T>() {
+                let active_item = dock.update(cx, |dock, cx| {
+                    dock.set_open(true, cx);
+                    dock.activate_panel(panel_index, cx);
+                    dock.active_panel().cloned()
+                });
+                if let Some(active_item) = active_item {
+                    if active_item.has_focus(cx) {
+                        cx.focus_self();
+                    } else {
+                        cx.focus(active_item.as_any());
+                    }
+                }
+
+                self.serialize_workspace(cx);
+                cx.notify();
+                break;
             }
         }
-
-        self.serialize_workspace(cx);
-
-        cx.notify();
     }
 
     fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {

crates/zed/src/zed.rs 🔗

@@ -31,14 +31,11 @@ use serde::Deserialize;
 use serde_json::to_string_pretty;
 use settings::{Settings, DEFAULT_SETTINGS_ASSET_PATH};
 use std::{borrow::Cow, str, sync::Arc};
-use terminal_view::terminal_panel::TerminalPanel;
+use terminal_view::terminal_panel::{self, TerminalPanel};
 use util::{channel::ReleaseChannel, paths, ResultExt};
 use uuid::Uuid;
 pub use workspace;
-use workspace::{
-    create_and_open_local_file, dock::DockPosition, open_new, AppState, NewFile, NewWindow,
-    Workspace,
-};
+use workspace::{create_and_open_local_file, open_new, AppState, NewFile, NewWindow, Workspace};
 
 #[derive(Deserialize, Clone, PartialEq)]
 pub struct OpenBrowser {
@@ -242,7 +239,14 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
         |workspace: &mut Workspace,
          _: &project_panel::ToggleFocus,
          cx: &mut ViewContext<Workspace>| {
-            workspace.toggle_panel_focus(DockPosition::Left, 0, cx);
+            workspace.toggle_panel_focus::<ProjectPanel>(cx);
+        },
+    );
+    cx.add_action(
+        |workspace: &mut Workspace,
+         _: &terminal_panel::ToggleFocus,
+         cx: &mut ViewContext<Workspace>| {
+            workspace.toggle_panel_focus::<TerminalPanel>(cx);
         },
     );
     cx.add_global_action({