Fix on_action on focusable

Conrad Irwin created

We were accidentally dropping the key context

Change summary

crates/go_to_line2/src/go_to_line.rs |  7 +--
crates/gpui2/src/elements/div.rs     |  6 +-
crates/gpui2/src/interactive.rs      |  5 +-
crates/gpui2/src/key_dispatch.rs     | 55 ++++++++++-------------------
crates/gpui2/src/window.rs           |  6 +-
crates/picker2/src/picker2.rs        |  9 +---
6 files changed, 34 insertions(+), 54 deletions(-)

Detailed changes

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -1,8 +1,7 @@
 use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
     actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
-    StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext,
-    VisualContext, WindowContext,
+    StatelessInteractive, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
 };
 use text::{Bias, Point};
 use theme::ActiveTheme;
@@ -146,11 +145,11 @@ impl GoToLine {
 }
 
 impl Render for GoToLine {
-    type Element = Div<Self, StatefulInteractivity<Self>>;
+    type Element = Div<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         modal(cx)
-            .id("go to line")
+            .context("GoToLine")
             .on_action(Self::cancel)
             .on_action(Self::confirm)
             .w_96()

crates/gpui2/src/elements/div.rs 🔗

@@ -128,7 +128,7 @@ impl<V: 'static> Div<V, StatefulInteractivity<V>, NonFocusableKeyDispatch> {
     pub fn focusable(self) -> Div<V, StatefulInteractivity<V>, FocusableKeyDispatch<V>> {
         Div {
             interactivity: self.interactivity,
-            key_dispatch: FocusableKeyDispatch::new(),
+            key_dispatch: FocusableKeyDispatch::new(self.key_dispatch),
             children: self.children,
             group: self.group,
             base_style: self.base_style,
@@ -141,7 +141,7 @@ impl<V: 'static> Div<V, StatefulInteractivity<V>, NonFocusableKeyDispatch> {
     ) -> Div<V, StatefulInteractivity<V>, FocusableKeyDispatch<V>> {
         Div {
             interactivity: self.interactivity,
-            key_dispatch: FocusableKeyDispatch::tracked(handle),
+            key_dispatch: FocusableKeyDispatch::tracked(self.key_dispatch, handle),
             children: self.children,
             group: self.group,
             base_style: self.base_style,
@@ -172,7 +172,7 @@ impl<V: 'static> Div<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
     ) -> Div<V, StatefulInteractivity<V>, FocusableKeyDispatch<V>> {
         Div {
             interactivity: self.interactivity.into_stateful(handle),
-            key_dispatch: handle.clone().into(),
+            key_dispatch: FocusableKeyDispatch::tracked(self.key_dispatch, handle),
             children: self.children,
             group: self.group,
             base_style: self.base_style,

crates/gpui2/src/interactive.rs 🔗

@@ -1247,9 +1247,10 @@ mod test {
         fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
             div().id("testview").child(
                 div()
+                    .context("test")
+                    .track_focus(&self.focus_handle)
                     .on_key_down(|this: &mut TestView, _, _, _| this.saw_key_down = true)
-                    .on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true)
-                    .track_focus(&self.focus_handle),
+                    .on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true),
             )
         }
     }

crates/gpui2/src/key_dispatch.rs 🔗

@@ -33,7 +33,7 @@ pub(crate) struct DispatchTree {
 #[derive(Default)]
 pub(crate) struct DispatchNode {
     pub key_listeners: SmallVec<[KeyListener; 2]>,
-    pub action_listeners: SmallVec<[ActionListener; 16]>,
+    pub action_listeners: SmallVec<[DispatchActionListener; 16]>,
     pub context: KeyContext,
     parent: Option<DispatchNodeId>,
 }
@@ -41,7 +41,7 @@ pub(crate) struct DispatchNode {
 type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>;
 
 #[derive(Clone)]
-pub(crate) struct ActionListener {
+pub(crate) struct DispatchActionListener {
     pub(crate) action_type: TypeId,
     pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
 }
@@ -102,10 +102,12 @@ impl DispatchTree {
         action_type: TypeId,
         listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
     ) {
-        self.active_node().action_listeners.push(ActionListener {
-            action_type,
-            listener,
-        });
+        self.active_node()
+            .action_listeners
+            .push(DispatchActionListener {
+                action_type,
+                listener,
+            });
     }
 
     pub fn make_focusable(&mut self, focus_id: FocusId) {
@@ -135,7 +137,7 @@ impl DispatchTree {
         if let Some(node) = self.focusable_node_ids.get(&target) {
             for node_id in self.dispatch_path(*node) {
                 let node = &self.nodes[node_id.0];
-                for ActionListener { action_type, .. } in &node.action_listeners {
+                for DispatchActionListener { action_type, .. } in &node.action_listeners {
                     actions.extend(build_action_from_type(action_type).log_err());
                 }
             }
@@ -148,21 +150,15 @@ impl DispatchTree {
         keystroke: &Keystroke,
         context: &[KeyContext],
     ) -> Option<Box<dyn Action>> {
-        if !self
-            .keystroke_matchers
-            .contains_key(self.context_stack.as_slice())
-        {
-            let keystroke_contexts = self.context_stack.iter().cloned().collect();
+        if !self.keystroke_matchers.contains_key(context) {
+            let keystroke_contexts = context.iter().cloned().collect();
             self.keystroke_matchers.insert(
                 keystroke_contexts,
                 KeystrokeMatcher::new(self.keymap.clone()),
             );
         }
 
-        let keystroke_matcher = self
-            .keystroke_matchers
-            .get_mut(self.context_stack.as_slice())
-            .unwrap();
+        let keystroke_matcher = self.keystroke_matchers.get_mut(context).unwrap();
         if let KeyMatch::Some(action) = keystroke_matcher.match_keystroke(keystroke, context) {
             // Clear all pending keystrokes when an action has been found.
             for keystroke_matcher in self.keystroke_matchers.values_mut() {
@@ -274,7 +270,7 @@ pub trait KeyDispatch<V: 'static>: 'static {
 }
 
 pub struct FocusableKeyDispatch<V> {
-    pub key_context: KeyContext,
+    pub non_focusable: NonFocusableKeyDispatch,
     pub focus_handle: Option<FocusHandle>,
     pub focus_listeners: FocusListeners<V>,
     pub focus_style: StyleRefinement,
@@ -283,9 +279,9 @@ pub struct FocusableKeyDispatch<V> {
 }
 
 impl<V> FocusableKeyDispatch<V> {
-    pub fn new() -> Self {
+    pub fn new(non_focusable: NonFocusableKeyDispatch) -> Self {
         Self {
-            key_context: KeyContext::default(),
+            non_focusable,
             focus_handle: None,
             focus_listeners: FocusListeners::default(),
             focus_style: StyleRefinement::default(),
@@ -294,9 +290,9 @@ impl<V> FocusableKeyDispatch<V> {
         }
     }
 
-    pub fn tracked(handle: &FocusHandle) -> Self {
+    pub fn tracked(non_focusable: NonFocusableKeyDispatch, handle: &FocusHandle) -> Self {
         Self {
-            key_context: KeyContext::default(),
+            non_focusable,
             focus_handle: Some(handle.clone()),
             focus_listeners: FocusListeners::default(),
             focus_style: StyleRefinement::default(),
@@ -316,24 +312,11 @@ impl<V: 'static> KeyDispatch<V> for FocusableKeyDispatch<V> {
     }
 
     fn key_context(&self) -> &KeyContext {
-        &self.key_context
+        &self.non_focusable.key_context
     }
 
     fn key_context_mut(&mut self) -> &mut KeyContext {
-        &mut self.key_context
-    }
-}
-
-impl<V> From<FocusHandle> for FocusableKeyDispatch<V> {
-    fn from(value: FocusHandle) -> Self {
-        Self {
-            key_context: KeyContext::default(),
-            focus_handle: Some(value),
-            focus_listeners: FocusListeners::default(),
-            focus_style: StyleRefinement::default(),
-            focus_in_style: StyleRefinement::default(),
-            in_focus_style: StyleRefinement::default(),
-        }
+        &mut self.non_focusable.key_context
     }
 }
 

crates/gpui2/src/window.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    key_dispatch::ActionListener, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext,
+    key_dispatch::DispatchActionListener, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext,
     AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
     DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
     EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
@@ -1306,7 +1306,7 @@ impl<'a> WindowContext<'a> {
         // Capture phase
         for node_id in &dispatch_path {
             let node = self.window.current_frame.dispatch_tree.node(*node_id);
-            for ActionListener {
+            for DispatchActionListener {
                 action_type,
                 listener,
             } in node.action_listeners.clone()
@@ -1324,7 +1324,7 @@ impl<'a> WindowContext<'a> {
         // Bubble phase
         for node_id in dispatch_path.iter().rev() {
             let node = self.window.current_frame.dispatch_tree.node(*node_id);
-            for ActionListener {
+            for DispatchActionListener {
                 action_type,
                 listener,
             } in node.action_listeners.clone()

crates/picker2/src/picker2.rs 🔗

@@ -1,8 +1,7 @@
 use editor::Editor;
 use gpui::{
-    div, uniform_list, Component, Div, FocusableKeyDispatch, ParentElement, Render,
-    StatefulInteractivity, StatelessInteractive, Styled, Task, UniformListScrollHandle, View,
-    ViewContext, VisualContext, WindowContext,
+    div, uniform_list, Component, Div, ParentElement, Render, StatelessInteractive, Styled, Task,
+    UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
 };
 use std::cmp;
 use theme::ActiveTheme;
@@ -137,13 +136,11 @@ impl<D: PickerDelegate> Picker<D> {
 }
 
 impl<D: PickerDelegate> Render for Picker<D> {
-    type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
+    type Element = Div<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div()
             .context("picker")
-            .id("picker-container")
-            .focusable()
             .size_full()
             .on_action(Self::select_next)
             .on_action(Self::select_prev)