Fixed focus issues with scrolling and input

Mikayla Maki created

Change summary

assets/keymaps/default.json     |  1 
crates/gpui/src/presenter.rs    | 14 ++++++++
crates/terminal/src/element.rs  | 56 +++++++++++++++++++++++++++-------
crates/terminal/src/terminal.rs | 41 ++++++++++++++++---------
4 files changed, 84 insertions(+), 28 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -366,7 +366,6 @@
             "up": "terminal::UP",
             "down": "terminal::DOWN",
             "tab": "terminal::TAB",
-            "cmd-k": "terminal::Clear",
             "cmd-v": "terminal::Paste"
         }
     }

crates/gpui/src/presenter.rs 🔗

@@ -703,6 +703,20 @@ impl<'a> EventContext<'a> {
         self.view_stack.last().copied()
     }
 
+    pub fn is_parent_view_focused(&self) -> bool {
+        if let Some(parent_view_id) = self.view_stack.last() {
+            self.app.focused_view_id(self.window_id) == Some(*parent_view_id)
+        } else {
+            false
+        }
+    }
+
+    pub fn focus_parent_view(&mut self) {
+        if let Some(parent_view_id) = self.view_stack.last() {
+            self.app.focus(self.window_id, Some(*parent_view_id))
+        }
+    }
+
     pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
         self.dispatched_actions.push(DispatchDirective {
             dispatcher_view_id: self.view_stack.last().copied(),

crates/terminal/src/element.rs 🔗

@@ -17,12 +17,12 @@ use gpui::{
     geometry::{rect::RectF, vector::vec2f},
     json::json,
     text_layout::Line,
-    Event, Quad,
+    Event, MouseRegion, Quad,
 };
 use mio_extras::channel::Sender;
 use ordered_float::OrderedFloat;
 use settings::Settings;
-use std::sync::Arc;
+use std::{rc::Rc, sync::Arc};
 use theme::TerminalStyle;
 
 use crate::{Input, ZedListener};
@@ -33,6 +33,7 @@ pub struct TerminalEl {
     term: Arc<FairMutex<Term<ZedListener>>>,
     pty_tx: Sender<Msg>,
     size: SizeInfo,
+    view_id: usize,
 }
 
 impl TerminalEl {
@@ -40,8 +41,14 @@ impl TerminalEl {
         term: Arc<FairMutex<Term<ZedListener>>>,
         pty_tx: Sender<Msg>,
         size: SizeInfo,
+        view_id: usize,
     ) -> TerminalEl {
-        TerminalEl { term, pty_tx, size }
+        TerminalEl {
+            term,
+            pty_tx,
+            size,
+            view_id,
+        }
     }
 }
 
@@ -183,6 +190,21 @@ impl Element for TerminalEl {
         cx: &mut gpui::PaintContext,
     ) -> Self::PaintState {
         cx.scene.push_layer(Some(visible_bounds));
+
+        cx.scene.push_mouse_region(MouseRegion {
+            view_id: self.view_id,
+            discriminant: None,
+            bounds: visible_bounds,
+            hover: None,
+            mouse_down: Some(Rc::new(|_, cx| cx.focus_parent_view())),
+            click: None,
+            right_mouse_down: None,
+            right_click: None,
+            drag: None,
+            mouse_down_out: None,
+            right_mouse_down_out: None,
+        });
+
         let origin = bounds.origin() + vec2f(layout.em_width, 0.);
 
         let mut line_origin = origin;
@@ -214,24 +236,34 @@ impl Element for TerminalEl {
         &mut self,
         event: &gpui::Event,
         _bounds: gpui::geometry::rect::RectF,
-        _visible_bounds: gpui::geometry::rect::RectF,
+        visible_bounds: gpui::geometry::rect::RectF,
         layout: &mut Self::LayoutState,
         _paint: &mut Self::PaintState,
         cx: &mut gpui::EventContext,
     ) -> bool {
         match event {
-            Event::ScrollWheel { delta, .. } => {
-                let vertical_scroll =
-                    (delta.y() / layout.line_height) * ALACRITTY_SCROLL_MULTIPLIER;
-                let scroll = Scroll::Delta(vertical_scroll.round() as i32);
-                self.term.lock().scroll_display(scroll);
-                true
+            Event::ScrollWheel {
+                delta, position, ..
+            } => {
+                if visible_bounds.contains_point(*position) {
+                    let vertical_scroll =
+                        (delta.y() / layout.line_height) * ALACRITTY_SCROLL_MULTIPLIER;
+                    let scroll = Scroll::Delta(vertical_scroll.round() as i32);
+                    self.term.lock().scroll_display(scroll);
+                    true
+                } else {
+                    false
+                }
             }
             Event::KeyDown {
                 input: Some(input), ..
             } => {
-                cx.dispatch_action(Input(input.to_string()));
-                true
+                if cx.is_parent_view_focused() {
+                    cx.dispatch_action(Input(input.to_string()));
+                    true
+                } else {
+                    false
+                }
             }
             _ => false,
         }

crates/terminal/src/terminal.rs 🔗

@@ -84,13 +84,14 @@ struct Terminal {
     has_bell: bool, //Currently using iTerm bell, show bell emoji in tab until input is received
 }
 
-enum ZedTermEvent {
+enum Event {
     TitleChanged,
     CloseTerminal,
+    Activate,
 }
 
 impl Entity for Terminal {
-    type Event = ZedTermEvent;
+    type Event = Event;
 }
 
 impl Terminal {
@@ -166,7 +167,7 @@ impl Terminal {
                 if !cx.is_self_focused() {
                     //Need to figure out how to trigger a redraw when not in focus
                     self.has_new_content = true; //Change tab content
-                    cx.emit(ZedTermEvent::TitleChanged);
+                    cx.emit(Event::TitleChanged);
                 } else {
                     cx.notify()
                 }
@@ -180,11 +181,11 @@ impl Terminal {
             }
             AlacTermEvent::Title(title) => {
                 self.title = title;
-                cx.emit(ZedTermEvent::TitleChanged);
+                cx.emit(Event::TitleChanged);
             }
             AlacTermEvent::ResetTitle => {
                 self.title = DEFAULT_TITLE.to_string();
-                cx.emit(ZedTermEvent::TitleChanged);
+                cx.emit(Event::TitleChanged);
             }
             AlacTermEvent::ClipboardStore(_, data) => {
                 cx.write_to_clipboard(ClipboardItem::new(data))
@@ -209,7 +210,7 @@ impl Terminal {
             }
             AlacTermEvent::Bell => {
                 self.has_bell = true;
-                cx.emit(ZedTermEvent::TitleChanged);
+                cx.emit(Event::TitleChanged);
             }
             AlacTermEvent::Exit => self.quit(&Quit, cx),
         }
@@ -226,7 +227,7 @@ impl Terminal {
     }
 
     fn quit(&mut self, _: &Quit, cx: &mut ViewContext<Self>) {
-        cx.emit(ZedTermEvent::CloseTerminal);
+        cx.emit(Event::CloseTerminal);
     }
 
     fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
@@ -239,7 +240,7 @@ impl Terminal {
         //iTerm bell behavior, bell stays until terminal is interacted with
         self.has_bell = false;
         self.term.lock().scroll_display(Scroll::Bottom);
-        cx.emit(ZedTermEvent::TitleChanged);
+        cx.emit(Event::TitleChanged);
         self.pty_tx.notify(input.0.clone().into_bytes());
     }
 
@@ -301,13 +302,19 @@ impl View for Terminal {
         //TODO: derive this
         let size_info = SizeInfo::new(400., 100.0, 5., 5., 0., 0., false);
 
-        TerminalEl::new(self.term.clone(), self.pty_tx.0.clone(), size_info)
-            .contained()
-            // .with_style(theme.terminal.container)
-            .boxed()
+        TerminalEl::new(
+            self.term.clone(),
+            self.pty_tx.0.clone(),
+            size_info,
+            cx.view_id(),
+        )
+        .contained()
+        // .with_style(theme.terminal.container)
+        .boxed()
     }
 
-    fn on_focus(&mut self, _: &mut ViewContext<Self>) {
+    fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+        cx.emit(Event::Activate);
         self.has_new_content = false;
     }
 }
@@ -392,10 +399,14 @@ impl Item for Terminal {
     }
 
     fn should_update_tab_on_event(event: &Self::Event) -> bool {
-        matches!(event, &ZedTermEvent::TitleChanged)
+        matches!(event, &Event::TitleChanged)
     }
 
     fn should_close_item_on_event(event: &Self::Event) -> bool {
-        matches!(event, &ZedTermEvent::CloseTerminal)
+        matches!(event, &Event::CloseTerminal)
+    }
+
+    fn should_activate_item_on_event(event: &Self::Event) -> bool {
+        matches!(event, &Event::Activate)
     }
 }