Fix up keybindings propagation

Piotr Osiewicz and Conrad created

Co-authored-by: Conrad <conrad@zed.dev>

Change summary

assets/keymaps/default.json                    |  3 +--
crates/command_palette2/src/command_palette.rs |  5 ++---
crates/editor2/src/element.rs                  |  4 ++++
crates/gpui2/src/interactive.rs                |  1 +
crates/gpui2/src/keymap/binding.rs             | 13 +++++++++++++
crates/gpui2/src/keymap/matcher.rs             |  1 +
crates/workspace2/src/modal_layer.rs           | 20 +++++++++++++++-----
crates/workspace2/src/workspace2.rs            |  7 ++++---
8 files changed, 41 insertions(+), 13 deletions(-)

Detailed changes

assets/keymaps/default.json 🔗

@@ -387,8 +387,7 @@
     }
   },
   {
-    // todo!() fix context
-    // "context": "Workspace",
+    "context": "Workspace",
     "bindings": {
       "cmd-1": ["workspace::ActivatePane", 0],
       "cmd-2": ["workspace::ActivatePane", 1],

crates/command_palette2/src/command_palette.rs 🔗

@@ -3,8 +3,8 @@ use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
     actions, div, Action, AnyElement, AnyWindowHandle, AppContext, BorrowWindow, Div, Element,
-    EventEmitter, FocusHandle, Keystroke, ParentElement, Render, View, ViewContext, VisualContext,
-    WeakView,
+    EventEmitter, FocusHandle, Keystroke, ParentElement, Render, Styled, View, ViewContext,
+    VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use std::cmp::{self, Reverse};
@@ -60,7 +60,6 @@ impl Render for CommandPalette {
     type Element = Div<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-        dbg!("Rendering");
         modal(cx).w_96().child(self.picker.clone())
     }
 }

crates/editor2/src/element.rs 🔗

@@ -4149,12 +4149,16 @@ fn build_key_listeners(
         build_key_listener(
             move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| {
                 if phase == DispatchPhase::Bubble {
+                    dbg!(&dispatch_context);
                     if let KeyMatch::Some(action) = cx.match_keystroke(
                         &global_element_id,
                         &key_down.keystroke,
                         dispatch_context,
                     ) {
+                        dbg!("got action", &action);
                         return Some(action);
+                    } else {
+                        dbg!("not action");
                     }
                 }
 

crates/gpui2/src/interactive.rs 🔗

@@ -414,6 +414,7 @@ pub trait ElementInteractivity<V: 'static>: 'static {
                     Box::new(move |_, key_down, context, phase, cx| {
                         if phase == DispatchPhase::Bubble {
                             let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
+                            dbg!(&context);
                             if let KeyMatch::Some(action) =
                                 cx.match_keystroke(&global_id, &key_down.keystroke, context)
                             {

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

@@ -44,6 +44,19 @@ impl KeyBinding {
         pending_keystrokes: &[Keystroke],
         contexts: &[&DispatchContext],
     ) -> KeyMatch {
+        let should_debug = self.keystrokes.len() == 1
+            && self.keystrokes[0].key == "p"
+            && self.keystrokes[0].modifiers.command == true
+            && self.keystrokes[0].modifiers.shift == true;
+
+        if false && should_debug {
+            dbg!(
+                &self.keystrokes,
+                &pending_keystrokes,
+                &contexts,
+                &self.matches_context(contexts)
+            );
+        }
         if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
             && self.matches_context(contexts)
         {

crates/gpui2/src/keymap/matcher.rs 🔗

@@ -46,6 +46,7 @@ impl KeyMatcher {
         keystroke: &Keystroke,
         context_stack: &[&DispatchContext],
     ) -> KeyMatch {
+        dbg!(keystroke, &context_stack);
         let keymap = self.keymap.lock();
         // Clear pending keystrokes if the keymap has changed since the last matched keystroke.
         if keymap.version() != self.keymap_version {

crates/workspace2/src/modal_layer.rs 🔗

@@ -1,7 +1,7 @@
 use crate::Workspace;
 use gpui::{
-    div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive,
-    Styled, Subscription, View, ViewContext,
+    div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatefulInteractivity,
+    StatelessInteractive, Styled, Subscription, View, ViewContext,
 };
 use std::{any::TypeId, sync::Arc};
 use ui::v_stack;
@@ -9,7 +9,14 @@ use ui::v_stack;
 pub struct ModalLayer {
     open_modal: Option<AnyView>,
     subscription: Option<Subscription>,
-    registered_modals: Vec<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
+    registered_modals: Vec<(
+        TypeId,
+        Box<
+            dyn Fn(
+                Div<Workspace, StatefulInteractivity<Workspace>>,
+            ) -> Div<Workspace, StatefulInteractivity<Workspace>>,
+        >,
+    )>,
 }
 
 pub enum ModalEvent {
@@ -64,8 +71,11 @@ impl ModalLayer {
         cx.notify();
     }
 
-    pub fn wrapper_element(&self, cx: &ViewContext<Workspace>) -> Div<Workspace> {
-        let mut parent = div().relative().size_full();
+    pub fn wrapper_element(
+        &self,
+        cx: &ViewContext<Workspace>,
+    ) -> Div<Workspace, StatefulInteractivity<Workspace>> {
+        let mut parent = div().id("modal layer").relative().size_full();
 
         for (_, action) in self.registered_modals.iter() {
             parent = (action)(parent);

crates/workspace2/src/workspace2.rs 🔗

@@ -39,8 +39,8 @@ use gpui::{
     actions, div, point, rems, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
     AsyncWindowContext, Bounds, Component, DispatchContext, Div, Entity, EntityId, EventEmitter,
     FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size,
-    StatefulInteractive, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView,
-    WindowBounds, WindowContext, WindowHandle, WindowOptions,
+    StatefulInteractive, StatefulInteractivity, Styled, Subscription, Task, View, ViewContext,
+    VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -3706,13 +3706,14 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
 impl EventEmitter<Event> for Workspace {}
 
 impl Render for Workspace {
-    type Element = Div<Self>;
+    type Element = Div<Self, StatefulInteractivity<Self>>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let mut context = DispatchContext::default();
         context.insert("Workspace");
         cx.with_key_dispatch_context(context, |cx| {
             div()
+                .id("workspace")
                 .relative()
                 .size_full()
                 .flex()