diff --git a/crates/gpui3/src/action.rs b/crates/gpui3/src/action.rs index e8d113532d..6a21aee95c 100644 --- a/crates/gpui3/src/action.rs +++ b/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; fn as_any(&self) -> &dyn Any; } diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index c417bcb0dd..23aac6526f 100644 --- a/crates/gpui3/src/app.rs +++ b/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>, - keymap: Arc>, + pub(crate) keymap: Arc>, pub(crate) pending_notifications: HashSet, pending_effects: VecDeque, pub(crate) observers: SubscriberSet, diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 3a9c179cf9..20832a1ea3 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -33,7 +33,7 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement { } #[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; diff --git a/crates/gpui3/src/keymap/binding.rs b/crates/gpui3/src/keymap/binding.rs index 2ac3c10964..2860ef52e7 100644 --- a/crates/gpui3/src/keymap/binding.rs +++ b/crates/gpui3/src/keymap/binding.rs @@ -63,7 +63,7 @@ impl KeyBinding { action: &dyn Action, contexts: &[ActionContext], ) -> Option> { - 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 diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 02160d6832..0f818bb4f0 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1035,8 +1035,21 @@ pub trait BorrowWindow: BorrowAppContext { id: impl Into, 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 diff --git a/crates/storybook2/src/stories/focus.rs b/crates/storybook2/src/stories/focus.rs index 3b1d08264c..3f75631cda 100644 --- a/crates/storybook2/src/stories/focus.rs +++ b/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::().is_some() + } + + fn boxed_clone(&self) -> Box { + 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::().is_some() + } + + fn boxed_clone(&self) -> Box { + 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()