Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/action.rs             |  2 
crates/gpui3/src/app.rs                |  2 
crates/gpui3/src/element.rs            |  2 
crates/gpui3/src/keymap/binding.rs     |  2 
crates/gpui3/src/window.rs             | 17 +++++++
crates/storybook2/src/stories/focus.rs | 59 +++++++++++++++++++++++++++
6 files changed, 77 insertions(+), 7 deletions(-)

Detailed changes

crates/gpui3/src/action.rs 🔗

@@ -4,7 +4,7 @@ use collections::{HashMap, HashSet};
 use std::any::Any;
 
 pub trait Action: Any + Send + Sync {
-    fn partial_eq(&self, action: &dyn Action) -> bool;
+    fn eq(&self, action: &dyn Action) -> bool;
     fn boxed_clone(&self) -> Box<dyn Action>;
     fn as_any(&self) -> &dyn Any;
 }

crates/gpui3/src/app.rs 🔗

@@ -112,7 +112,7 @@ pub struct AppContext {
     pub(crate) unit_entity: Handle<()>,
     pub(crate) entities: EntityMap,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
-    keymap: Arc<RwLock<Keymap>>,
+    pub(crate) keymap: Arc<RwLock<Keymap>>,
     pub(crate) pending_notifications: HashSet<EntityId>,
     pending_effects: VecDeque<Effect>,
     pub(crate) observers: SubscriberSet<EntityId, Handler>,

crates/gpui3/src/element.rs 🔗

@@ -33,7 +33,7 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
 }
 
 #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
-pub struct GlobalElementId(SmallVec<[ElementId; 8]>);
+pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 
 pub trait ElementIdentity: 'static + Send + Sync {
     fn id(&self) -> Option<ElementId>;

crates/gpui3/src/keymap/binding.rs 🔗

@@ -63,7 +63,7 @@ impl KeyBinding {
         action: &dyn Action,
         contexts: &[ActionContext],
     ) -> Option<SmallVec<[Keystroke; 2]>> {
-        if self.action.partial_eq(action) && self.matches_context(contexts) {
+        if self.action.eq(action) && self.matches_context(contexts) {
             Some(self.keystrokes.clone())
         } else {
             None

crates/gpui3/src/window.rs 🔗

@@ -1035,8 +1035,21 @@ pub trait BorrowWindow: BorrowAppContext {
         id: impl Into<ElementId>,
         f: impl FnOnce(GlobalElementId, &mut Self) -> R,
     ) -> R {
-        self.window_mut().element_id_stack.push(id.into());
-        let global_id = self.window_mut().element_id_stack.clone();
+        let keymap = self.app_mut().keymap.clone();
+        let window = self.window_mut();
+        window.element_id_stack.push(id.into());
+        let global_id = window.element_id_stack.clone();
+
+        if window.key_matchers.get(&global_id).is_none() {
+            window.key_matchers.insert(
+                global_id.clone(),
+                window
+                    .prev_frame_key_matchers
+                    .remove(&global_id)
+                    .unwrap_or_else(|| KeyMatcher::new(keymap)),
+            );
+        }
+
         let result = f(global_id, self);
         self.window_mut().element_id_stack.pop();
         result

crates/storybook2/src/stories/focus.rs 🔗

@@ -1,13 +1,56 @@
-use gpui3::{div, view, Context, Focus, ParentElement, Styled, View, WindowContext};
+use std::any::Any;
+
+use gpui3::{
+    div, view, Action, Context, Focus, Interactive, KeyBinding, ParentElement, Styled, View,
+    WindowContext,
+};
 
 use crate::themes::rose_pine;
 
+#[derive(Clone)]
+struct ActionA;
+
+impl Action for ActionA {
+    fn eq(&self, action: &dyn Action) -> bool {
+        action.as_any().downcast_ref::<Self>().is_some()
+    }
+
+    fn boxed_clone(&self) -> Box<dyn Action> {
+        Box::new(self.clone())
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
+#[derive(Clone)]
+struct ActionB;
+
+impl Action for ActionB {
+    fn eq(&self, action: &dyn Action) -> bool {
+        action.as_any().downcast_ref::<Self>().is_some()
+    }
+
+    fn boxed_clone(&self) -> Box<dyn Action> {
+        Box::new(self.clone())
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
 pub struct FocusStory {
     text: View<()>,
 }
 
 impl FocusStory {
     pub fn view(cx: &mut WindowContext) -> View<()> {
+        cx.bind_keys([
+            KeyBinding::new("cmd-a", ActionA, None),
+            KeyBinding::new("cmd-b", ActionB, None),
+        ]);
         let theme = rose_pine();
 
         let color_1 = theme.lowest.negative.default.foreground;
@@ -22,6 +65,12 @@ impl FocusStory {
         let child_2 = cx.focus_handle();
         view(cx.entity(|cx| ()), move |_, cx| {
             div()
+                .on_action(|_, action: &ActionA, phase, cx| {
+                    println!("Action A dispatched on parent during {:?}", phase);
+                })
+                .on_action(|_, action: &ActionB, phase, cx| {
+                    println!("Action A dispatched on parent during {:?}", phase);
+                })
                 .focusable(&parent)
                 .on_focus(|_, _, _| println!("Parent focused"))
                 .on_blur(|_, _, _| println!("Parent blurred"))
@@ -39,6 +88,10 @@ impl FocusStory {
                 .focus_in(|style| style.bg(color_3))
                 .child(
                     div()
+                        .id("child 1")
+                        .on_action(|_, action: &ActionA, phase, cx| {
+                            println!("Action A dispatched on child 1 during {:?}", phase);
+                        })
                         .focusable(&child_1)
                         .w_full()
                         .h_6()
@@ -59,6 +112,10 @@ impl FocusStory {
                 )
                 .child(
                     div()
+                        .id("child 2")
+                        .on_action(|_, action: &ActionB, phase, cx| {
+                            println!("Action B dispatched on child 2 during {:?}", phase);
+                        })
                         .focusable(&child_2)
                         .w_full()
                         .h_6()