Preserve stateless interactivity when assigning elements an id

Max Brunsfeld , Nathan , and Piotr created

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Piotr <piotr@zed.dev>

Change summary

crates/go_to_line2/src/go_to_line.rs      |  1 
crates/gpui2/src/elements/div.rs          |  2 
crates/gpui2/src/elements/uniform_list.rs |  2 
crates/gpui2/src/interactive.rs           | 54 +++++++++++-------------
crates/gpui2/src/window.rs                |  6 --
5 files changed, 28 insertions(+), 37 deletions(-)

Detailed changes

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -27,7 +27,6 @@ impl Render for GoToLine {
     type Element = Div<Self>;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
-        dbg!("rendering GoToLine");
         div().bg(red()).w(px(100.0)).h(px(100.0))
     }
 }

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

@@ -37,7 +37,7 @@ where
 {
     pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteractivity<V>, F> {
         Div {
-            interactivity: id.into().into(),
+            interactivity: StatefulInteractivity::new(id.into(), self.interactivity),
             focus: self.focus,
             children: self.children,
             group: self.group,

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

@@ -30,7 +30,7 @@ where
                 .map(|component| component.render())
                 .collect()
         }),
-        interactivity: id.into(),
+        interactivity: StatefulInteractivity::new(id, StatelessInteractivity::default()),
         scroll_handle: None,
     }
 }

crates/gpui2/src/interactive.rs 🔗

@@ -189,10 +189,10 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
     {
         self.stateless_interactivity().key_listeners.push((
             TypeId::of::<A>(),
-            Box::new(move |view, event, _, phase, cx| {
-                let event = event.downcast_ref().unwrap();
+            Box::new(move |view, action, _dipatch_context, phase, cx| {
+                let action = action.downcast_ref().unwrap();
                 if phase == DispatchPhase::Capture {
-                    listener(view, event, cx)
+                    listener(view, action, cx)
                 }
                 None
             }),
@@ -210,10 +210,10 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
     {
         self.stateless_interactivity().key_listeners.push((
             TypeId::of::<A>(),
-            Box::new(move |view, event, _, phase, cx| {
-                let event = event.downcast_ref().unwrap();
+            Box::new(move |view, action, _dispatch_context, phase, cx| {
+                let action = action.downcast_ref().unwrap();
                 if phase == DispatchPhase::Bubble {
-                    listener(view, event, cx)
+                    listener(view, action, cx)
                 }
 
                 None
@@ -407,6 +407,8 @@ pub trait ElementInteractivity<V: 'static>: 'static {
     ) -> R {
         if let Some(stateful) = self.as_stateful_mut() {
             cx.with_element_id(stateful.id.clone(), |global_id, cx| {
+                // In addition to any key down/up listeners registered directly on the element,
+                // we also add a key listener to match actions from the keymap.
                 stateful.key_listeners.push((
                     TypeId::of::<KeyDownEvent>(),
                     Box::new(move |_, key_down, context, phase, cx| {
@@ -774,6 +776,21 @@ pub struct StatefulInteractivity<V> {
     tooltip_builder: Option<TooltipBuilder<V>>,
 }
 
+impl<V: 'static> StatefulInteractivity<V> {
+    pub fn new(id: ElementId, stateless: StatelessInteractivity<V>) -> Self {
+        Self {
+            id,
+            stateless,
+            click_listeners: SmallVec::new(),
+            active_style: StyleRefinement::default(),
+            group_active_style: None,
+            drag_listener: None,
+            hover_listener: None,
+            tooltip_builder: None,
+        }
+    }
+}
+
 impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
     fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
         Some(self)
@@ -792,21 +809,6 @@ impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
     }
 }
 
-impl<V> From<ElementId> for StatefulInteractivity<V> {
-    fn from(id: ElementId) -> Self {
-        Self {
-            id,
-            stateless: StatelessInteractivity::default(),
-            click_listeners: SmallVec::new(),
-            drag_listener: None,
-            hover_listener: None,
-            tooltip_builder: None,
-            active_style: StyleRefinement::default(),
-            group_active_style: None,
-        }
-    }
-}
-
 type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
 
 pub struct StatelessInteractivity<V> {
@@ -1284,14 +1286,8 @@ mod test {
         fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
             div().id("testview").child(
                 div()
-                    .on_key_down(|this: &mut TestView, _, _, _| {
-                        dbg!("ola!");
-                        this.saw_key_down = true
-                    })
-                    .on_action(|this: &mut TestView, _: &TestAction, _| {
-                        dbg!("ola!");
-                        this.saw_action = true
-                    })
+                    .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),
             )
         }

crates/gpui2/src/window.rs 🔗

@@ -1170,7 +1170,6 @@ impl<'a> WindowContext<'a> {
                     .insert(any_mouse_event.type_id(), handlers);
             }
         } else if let Some(any_key_event) = event.keyboard_event() {
-            let mut did_handle_action = false;
             let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack);
             let key_event_type = any_key_event.type_id();
             let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new();
@@ -1191,7 +1190,6 @@ impl<'a> WindowContext<'a> {
                                 self.dispatch_action(action, &key_dispatch_stack[..ix]);
                             }
                             if !self.app.propagate_event {
-                                did_handle_action = true;
                                 break;
                             }
                         }
@@ -1220,7 +1218,6 @@ impl<'a> WindowContext<'a> {
                                 }
 
                                 if !self.app.propagate_event {
-                                    did_handle_action = true;
                                     break;
                                 }
                             }
@@ -1234,10 +1231,9 @@ impl<'a> WindowContext<'a> {
 
             drop(context_stack);
             self.window.key_dispatch_stack = key_dispatch_stack;
-            return did_handle_action;
         }
 
-        true
+        !self.app.propagate_event
     }
 
     /// Attempt to map a keystroke to an action based on the keymap.