Add new terminal when the terminal panel is activated, and not on focus

Antonio Scandurra created

Change summary

crates/project_panel/src/project_panel.rs  |  2 
crates/terminal_view/src/terminal_panel.rs | 61 ++++++++++++-----------
crates/workspace/src/dock.rs               | 33 +++++++++---
3 files changed, 58 insertions(+), 38 deletions(-)

Detailed changes

crates/project_panel/src/project_panel.rs 🔗

@@ -1401,6 +1401,8 @@ impl workspace::dock::Panel for ProjectPanel {
 
     fn set_zoomed(&mut self, _: bool, _: &mut ViewContext<Self>) {}
 
+    fn set_active(&mut self, _: bool, _: &mut ViewContext<Self>) {}
+
     fn icon_path(&self) -> &'static str {
         "icons/folder_tree_16.svg"
     }

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -1,9 +1,8 @@
 use crate::TerminalView;
 use gpui::{
-    actions, elements::*, AppContext, Entity, ModelHandle, Subscription, View, ViewContext,
-    ViewHandle, WeakViewHandle, WindowContext,
+    actions, anyhow, elements::*, AppContext, Entity, Subscription, View, ViewContext, ViewHandle,
+    WeakViewHandle, WindowContext,
 };
-use project::Project;
 use settings::{settings_file::SettingsFile, Settings, TerminalDockPosition, WorkingDirectory};
 use util::ResultExt;
 use workspace::{
@@ -26,7 +25,6 @@ pub enum Event {
 }
 
 pub struct TerminalPanel {
-    project: ModelHandle<Project>,
     pane: ViewHandle<Pane>,
     workspace: WeakViewHandle<Workspace>,
     _subscriptions: Vec<Subscription>,
@@ -86,7 +84,6 @@ impl TerminalPanel {
             cx.subscribe(&pane, Self::handle_pane_event),
         ];
         Self {
-            project: workspace.project().clone(),
             pane,
             workspace: workspace.weak_handle(),
             _subscriptions: subscriptions,
@@ -109,30 +106,34 @@ impl TerminalPanel {
     }
 
     fn add_terminal(&mut self, _: &workspace::NewTerminal, cx: &mut ViewContext<Self>) {
-        if let Some(workspace) = self.workspace.upgrade(cx) {
-            let working_directory_strategy = cx
-                .global::<Settings>()
-                .terminal_overrides
-                .working_directory
-                .clone()
-                .unwrap_or(WorkingDirectory::CurrentProjectDirectory);
-            let working_directory =
-                crate::get_working_directory(workspace.read(cx), cx, working_directory_strategy);
-            let window_id = cx.window_id();
-            if let Some(terminal) = self.project.update(cx, |project, cx| {
-                project
-                    .create_terminal(working_directory, window_id, cx)
-                    .log_err()
-            }) {
-                workspace.update(cx, |workspace, cx| {
+        let workspace = self.workspace.clone();
+        cx.spawn(|this, mut cx| async move {
+            let pane = this.read_with(&cx, |this, _| this.pane.clone())?;
+            workspace.update(&mut cx, |workspace, cx| {
+                let working_directory_strategy = cx
+                    .global::<Settings>()
+                    .terminal_overrides
+                    .working_directory
+                    .clone()
+                    .unwrap_or(WorkingDirectory::CurrentProjectDirectory);
+                let working_directory =
+                    crate::get_working_directory(workspace, cx, working_directory_strategy);
+                let window_id = cx.window_id();
+                if let Some(terminal) = workspace.project().update(cx, |project, cx| {
+                    project
+                        .create_terminal(working_directory, window_id, cx)
+                        .log_err()
+                }) {
                     let terminal =
                         Box::new(cx.add_view(|cx| {
                             TerminalView::new(terminal, workspace.database_id(), cx)
                         }));
-                    Pane::add_item(workspace, &self.pane, terminal, true, true, None, cx);
-                });
-            }
-        }
+                    Pane::add_item(workspace, &pane, terminal, true, true, None, cx);
+                }
+            })?;
+            anyhow::Ok(())
+        })
+        .detach_and_log_err(cx);
     }
 }
 
@@ -150,10 +151,6 @@ impl View for TerminalPanel {
     }
 
     fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
-        if self.pane.read(cx).items_len() == 0 {
-            self.add_terminal(&Default::default(), cx)
-        }
-
         if cx.is_self_focused() {
             cx.focus(&self.pane);
         }
@@ -215,6 +212,12 @@ impl Panel for TerminalPanel {
         self.pane.update(cx, |pane, cx| pane.set_zoomed(zoomed, cx));
     }
 
+    fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
+        if active && self.pane.read(cx).items_len() == 0 {
+            self.add_terminal(&Default::default(), cx)
+        }
+    }
+
     fn icon_path(&self) -> &'static str {
         "icons/terminal_12.svg"
     }

crates/workspace/src/dock.rs 🔗

@@ -24,6 +24,7 @@ pub trait Panel: View {
     fn should_zoom_out_on_event(_: &Self::Event) -> bool;
     fn is_zoomed(&self, cx: &WindowContext) -> bool;
     fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>);
+    fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>);
     fn should_activate_on_event(_: &Self::Event) -> bool;
     fn should_close_on_event(_: &Self::Event) -> bool;
     fn has_focus(&self, cx: &WindowContext) -> bool;
@@ -37,6 +38,7 @@ pub trait PanelHandle {
     fn set_position(&self, position: DockPosition, cx: &mut WindowContext);
     fn is_zoomed(&self, cx: &WindowContext) -> bool;
     fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext);
+    fn set_active(&self, active: bool, cx: &mut WindowContext);
     fn default_size(&self, cx: &WindowContext) -> f32;
     fn icon_path(&self, cx: &WindowContext) -> &'static str;
     fn icon_tooltip(&self, cx: &WindowContext) -> String;
@@ -77,6 +79,10 @@ where
         self.update(cx, |this, cx| this.set_zoomed(zoomed, cx))
     }
 
+    fn set_active(&self, active: bool, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_active(active, cx))
+    }
+
     fn icon_path(&self, cx: &WindowContext) -> &'static str {
         self.read(cx).icon_path()
     }
@@ -202,12 +208,16 @@ impl Dock {
     pub fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
         if open != self.is_open {
             self.is_open = open;
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(open, cx);
+            }
+
             cx.notify();
         }
     }
 
     pub fn toggle_open(&mut self, cx: &mut ViewContext<Self>) {
-        self.is_open = !self.is_open;
+        self.set_open(!self.is_open, cx);
         cx.notify();
     }
 
@@ -297,17 +307,18 @@ impl Dock {
     }
 
     pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
-        self.active_panel_index = panel_ix;
-        cx.notify();
-    }
+        if panel_ix != self.active_panel_index {
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(false, cx);
+            }
 
-    pub fn toggle_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
-        if self.active_panel_index == panel_ix {
-            self.is_open = false;
-        } else {
             self.active_panel_index = panel_ix;
+            if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+                active_panel.panel.set_active(true, cx);
+            }
+
+            cx.notify();
         }
-        cx.notify();
     }
 
     pub fn active_panel(&self) -> Option<&Rc<dyn PanelHandle>> {
@@ -615,6 +626,10 @@ pub(crate) mod test {
             unimplemented!()
         }
 
+        fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {
+            unimplemented!()
+        }
+
         fn default_size(&self, _: &WindowContext) -> f32 {
             match self.position.axis() {
                 Axis::Horizontal => 300.,