Merge branch 'gpui2' of github.com:zed-industries/zed into gpui2

Marshall Bowers created

Change summary

crates/gpui3/src/action.rs         |   8 
crates/gpui3/src/element.rs        |  94 +++++++-
crates/gpui3/src/elements/div.rs   | 161 +++++---------
crates/gpui3/src/elements/img.rs   |  36 +-
crates/gpui3/src/elements/svg.rs   |  36 +-
crates/gpui3/src/events.rs         | 290 --------------------------
crates/gpui3/src/focus.rs          |  23 +
crates/gpui3/src/gpui3.rs          |   2 
crates/gpui3/src/interactive.rs    | 344 +++++++++++++++++++++++++++++--
crates/gpui3/src/keymap/binding.rs |   8 
crates/gpui3/src/keymap/matcher.rs |   6 
crates/gpui3/src/window.rs         | 197 +++++++++++++----
12 files changed, 662 insertions(+), 543 deletions(-)

Detailed changes

crates/gpui3/src/action.rs 🔗

@@ -10,14 +10,14 @@ pub trait Action: Any + Send + Sync {
 }
 
 #[derive(Clone, Debug, Default, Eq, PartialEq)]
-pub struct ActionContext {
+pub struct DispatchContext {
     set: HashSet<SharedString>,
     map: HashMap<SharedString, SharedString>,
 }
 
-impl ActionContext {
+impl DispatchContext {
     pub fn new() -> Self {
-        ActionContext {
+        DispatchContext {
             set: HashSet::default(),
             map: HashMap::default(),
         }
@@ -68,7 +68,7 @@ impl ActionContextPredicate {
         }
     }
 
-    pub fn eval(&self, contexts: &[ActionContext]) -> bool {
+    pub fn eval(&self, contexts: &[&DispatchContext]) -> bool {
         let Some(context) = contexts.first() else {
             return false;
         };

crates/gpui3/src/element.rs 🔗

@@ -1,5 +1,9 @@
-use crate::{BorrowWindow, Bounds, ElementId, FocusHandle, LayoutId, Pixels, Point, ViewContext};
+use crate::{
+    BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, LayoutId,
+    MouseDownEvent, Pixels, Point, Style, StyleRefinement, ViewContext, WindowContext,
+};
 use derive_more::{Deref, DerefMut};
+use refineable::Refineable;
 pub(crate) use smallvec::SmallVec;
 use std::mem;
 
@@ -55,34 +59,94 @@ impl ElementIdentity for Anonymous {
     }
 }
 
-pub trait ElementFocusability: 'static + Send + Sync {
-    fn focus_handle(&self) -> Option<&FocusHandle>;
-}
+pub trait ElementFocusability<V: 'static + Send + Sync>: 'static + Send + Sync {
+    fn as_focusable(&self) -> Option<&Focusable<V>>;
 
-pub struct Focusable(FocusHandle);
+    fn initialize<R>(
+        &self,
+        cx: &mut ViewContext<V>,
+        f: impl FnOnce(&mut ViewContext<V>) -> R,
+    ) -> R {
+        if let Some(focusable) = self.as_focusable() {
+            for listener in focusable.focus_listeners.iter().cloned() {
+                cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
+            }
+            cx.with_focus(focusable.focus_handle.clone(), |cx| f(cx))
+        } else {
+            f(cx)
+        }
+    }
 
-impl AsRef<FocusHandle> for Focusable {
-    fn as_ref(&self) -> &FocusHandle {
-        &self.0
+    fn refine_style(&self, style: &mut Style, cx: &WindowContext) {
+        if let Some(focusable) = self.as_focusable() {
+            if focusable.focus_handle.contains_focused(cx) {
+                style.refine(&focusable.focus_in_style);
+            }
+
+            if focusable.focus_handle.within_focused(cx) {
+                style.refine(&focusable.in_focus_style);
+            }
+
+            if focusable.focus_handle.is_focused(cx) {
+                style.refine(&focusable.focus_style);
+            }
+        }
+    }
+
+    fn paint(&self, bounds: Bounds<Pixels>, cx: &mut WindowContext) {
+        if let Some(focusable) = self.as_focusable() {
+            let focus_handle = focusable.focus_handle.clone();
+            cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
+                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
+                    if !cx.default_prevented() {
+                        cx.focus(&focus_handle);
+                        cx.prevent_default();
+                    }
+                }
+            })
+        }
     }
 }
 
-impl ElementFocusability for Focusable {
-    fn focus_handle(&self) -> Option<&FocusHandle> {
-        Some(&self.0)
+pub struct Focusable<V: 'static + Send + Sync> {
+    pub focus_handle: FocusHandle,
+    pub focus_listeners: FocusListeners<V>,
+    pub focus_style: StyleRefinement,
+    pub focus_in_style: StyleRefinement,
+    pub in_focus_style: StyleRefinement,
+}
+
+impl<V> ElementFocusability<V> for Focusable<V>
+where
+    V: 'static + Send + Sync,
+{
+    fn as_focusable(&self) -> Option<&Focusable<V>> {
+        Some(self)
     }
 }
 
-impl From<FocusHandle> for Focusable {
+impl<V> From<FocusHandle> for Focusable<V>
+where
+    V: 'static + Send + Sync,
+{
     fn from(value: FocusHandle) -> Self {
-        Self(value)
+        Self {
+            focus_handle: value,
+            focus_listeners: FocusListeners::default(),
+            focus_style: StyleRefinement::default(),
+            focus_in_style: StyleRefinement::default(),
+            in_focus_style: StyleRefinement::default(),
+        }
     }
 }
 
 pub struct NonFocusable;
 
-impl ElementFocusability for NonFocusable {
-    fn focus_handle(&self) -> Option<&FocusHandle> {
+impl<V> ElementFocusability<V> for NonFocusable
+where
+    V: 'static + Send + Sync,
+{
+    fn as_focusable(&self) -> Option<&Focusable<V>> {
         None
     }
 }

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

@@ -1,10 +1,10 @@
 use crate::{
     Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
-    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle, Focusable,
-    GlobalElementId, Hover, Identified, Interactive, IntoAnyElement, KeyDownEvent, KeyMatch,
-    LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable,
-    Overflow, ParentElement, Pixels, Point, ScrollWheelEvent, SharedString, Style, StyleRefinement,
-    Styled, ViewContext,
+    ElementFocusability, ElementId, ElementIdentity, Focus, FocusHandle, FocusListeners, Focusable,
+    GlobalElementId, Hover, Identified, Interactive, InteractiveState, IntoAnyElement,
+    KeyDownEvent, KeyMatch, LayoutId, MouseClickEvent, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, ScrollWheelEvent,
+    SharedString, Style, StyleRefinement, Styled, ViewContext,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
@@ -61,6 +61,23 @@ impl ScrollState {
     }
 }
 
+pub struct Div<
+    V: 'static + Send + Sync,
+    I: ElementIdentity = Anonymous,
+    F: ElementFocusability<V> = NonFocusable,
+> {
+    identity: I,
+    focusability: F,
+    children: SmallVec<[AnyElement<V>; 2]>,
+    group: Option<SharedString>,
+    base_style: StyleRefinement,
+    hover_style: StyleRefinement,
+    group_hover: Option<GroupStyle>,
+    active_style: StyleRefinement,
+    group_active: Option<GroupStyle>,
+    interactive_state: InteractiveState<V>,
+}
+
 pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
 where
     V: 'static + Send + Sync,
@@ -75,33 +92,10 @@ where
         group_hover: None,
         active_style: StyleRefinement::default(),
         group_active: None,
-        focus_style: StyleRefinement::default(),
-        focus_in_style: StyleRefinement::default(),
-        in_focus_style: StyleRefinement::default(),
-        listeners: EventListeners::default(),
+        interactive_state: InteractiveState::default(),
     }
 }
 
-pub struct Div<
-    V: 'static + Send + Sync,
-    I: ElementIdentity = Anonymous,
-    F: ElementFocusability = NonFocusable,
-> {
-    identity: I,
-    focusability: F,
-    children: SmallVec<[AnyElement<V>; 2]>,
-    group: Option<SharedString>,
-    base_style: StyleRefinement,
-    hover_style: StyleRefinement,
-    group_hover: Option<GroupStyle>,
-    active_style: StyleRefinement,
-    group_active: Option<GroupStyle>,
-    focus_style: StyleRefinement,
-    focus_in_style: StyleRefinement,
-    in_focus_style: StyleRefinement,
-    listeners: EventListeners<V>,
-}
-
 struct GroupStyle {
     group: SharedString,
     style: StyleRefinement,
@@ -109,7 +103,7 @@ struct GroupStyle {
 
 impl<V, F> Div<V, Anonymous, F>
 where
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     pub fn id(self, id: impl Into<ElementId>) -> Div<V, Identified, F> {
@@ -123,10 +117,7 @@ where
             group_hover: self.group_hover,
             active_style: self.active_style,
             group_active: self.group_active,
-            focus_style: self.focus_style,
-            focus_in_style: self.focus_in_style,
-            in_focus_style: self.in_focus_style,
-            listeners: self.listeners,
+            interactive_state: self.interactive_state,
         }
     }
 }
@@ -134,7 +125,7 @@ where
 impl<V, I, F> Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     pub fn group(mut self, group: impl Into<SharedString>) -> Self {
@@ -206,19 +197,7 @@ where
         let mut computed_style = Style::default();
         computed_style.refine(&self.base_style);
 
-        if let Some(handle) = self.focusability.focus_handle() {
-            if handle.contains_focused(cx) {
-                computed_style.refine(&self.focus_in_style);
-            }
-
-            if handle.within_focused(cx) {
-                computed_style.refine(&self.in_focus_style);
-            }
-
-            if handle.is_focused(cx) {
-                computed_style.refine(&self.focus_style);
-            }
-        }
+        self.focusability.refine_style(&mut computed_style, cx);
 
         let mouse_position = cx.mouse_position();
 
@@ -296,7 +275,7 @@ where
         pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
         cx: &mut ViewContext<V>,
     ) {
-        let click_listeners = mem::take(&mut self.listeners.mouse_click);
+        let click_listeners = mem::take(&mut self.interactive_state.mouse_click);
 
         let mouse_down = pending_click.lock().clone();
         if let Some(mouse_down) = mouse_down {
@@ -321,37 +300,25 @@ where
             });
         }
 
-        if let Some(focus_handle) = self.focusability.focus_handle() {
-            let focus_handle = focus_handle.clone();
-            cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    if !cx.default_prevented() {
-                        cx.focus(&focus_handle);
-                        cx.prevent_default();
-                    }
-                }
-            })
-        }
-
-        for listener in mem::take(&mut self.listeners.mouse_down) {
+        for listener in mem::take(&mut self.interactive_state.mouse_down) {
             cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.mouse_up) {
+        for listener in mem::take(&mut self.interactive_state.mouse_up) {
             cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.mouse_move) {
+        for listener in mem::take(&mut self.interactive_state.mouse_move) {
             cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in mem::take(&mut self.listeners.scroll_wheel) {
+        for listener in mem::take(&mut self.interactive_state.scroll_wheel) {
             cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
@@ -364,7 +331,7 @@ where
     I: ElementIdentity,
     V: 'static + Send + Sync,
 {
-    pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable> {
+    pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable<V>> {
         Div {
             identity: self.identity,
             focusability: handle.clone().into(),
@@ -375,40 +342,41 @@ where
             group_hover: self.group_hover,
             active_style: self.active_style,
             group_active: self.group_active,
-            focus_style: self.focus_style,
-            focus_in_style: self.focus_in_style,
-            in_focus_style: self.in_focus_style,
-            listeners: self.listeners,
+            interactive_state: self.interactive_state,
         }
     }
 }
 
-impl<V, I> Focus for Div<V, I, Focusable>
+impl<V, I> Focus for Div<V, I, Focusable<V>>
 where
     I: ElementIdentity,
     V: 'static + Send + Sync,
 {
+    fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
+        &mut self.focusability.focus_listeners
+    }
+
     fn handle(&self) -> &FocusHandle {
-        self.focusability.as_ref()
+        &self.focusability.focus_handle
     }
 
     fn set_focus_style(&mut self, style: StyleRefinement) {
-        self.focus_style = style;
+        self.focusability.focus_style = style;
     }
 
     fn set_focus_in_style(&mut self, style: StyleRefinement) {
-        self.focus_in_style = style;
+        self.focusability.focus_in_style = style;
     }
 
     fn set_in_focus_style(&mut self, style: StyleRefinement) {
-        self.in_focus_style = style;
+        self.focusability.in_focus_style = style;
     }
 }
 
 impl<V, I, F> Element for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     type ViewState = V;
@@ -426,20 +394,16 @@ where
     ) -> Self::ElementState {
         self.with_element_id(cx, |this, global_id, cx| {
             let element_state = element_state.unwrap_or_default();
-            for listener in this.listeners.focus.iter().cloned() {
-                cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
-            }
-
-            let mut key_listeners = mem::take(&mut this.listeners.key);
 
+            let mut key_listeners = mem::take(&mut this.interactive_state.key);
             if let Some(global_id) = global_id {
                 key_listeners.push((
                     TypeId::of::<KeyDownEvent>(),
-                    Arc::new(move |_, key_down, phase, cx| {
+                    Arc::new(move |_, key_down, context, phase, cx| {
                         if phase == DispatchPhase::Bubble {
                             let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
                             if let KeyMatch::Some(action) =
-                                cx.match_keystroke(&global_id, &key_down.keystroke)
+                                cx.match_keystroke(&global_id, &key_down.keystroke, context)
                             {
                                 return Some(action);
                             }
@@ -451,19 +415,13 @@ where
             }
 
             cx.with_key_listeners(&key_listeners, |cx| {
-                if let Some(focus_handle) = this.focusability.focus_handle().cloned() {
-                    cx.with_focus(focus_handle, |cx| {
-                        for child in &mut this.children {
-                            child.initialize(view_state, cx);
-                        }
-                    })
-                } else {
+                this.focusability.initialize(cx, |cx| {
                     for child in &mut this.children {
                         child.initialize(view_state, cx);
                     }
-                }
+                });
             });
-            this.listeners.key = key_listeners;
+            this.interactive_state.key = key_listeners;
 
             element_state
         })
@@ -526,6 +484,7 @@ where
                         element_state.active_state.clone(),
                         cx,
                     );
+                    this.focusability.paint(bounds, cx);
                     this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx);
                 });
 
@@ -554,7 +513,7 @@ where
 impl<V, I, F> IntoAnyElement<V> for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     fn into_any(self) -> AnyElement<V> {
@@ -565,7 +524,7 @@ where
 impl<V, I, F> ParentElement for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
@@ -576,7 +535,7 @@ where
 impl<V, I, F> Styled for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     fn style(&mut self) -> &mut StyleRefinement {
@@ -587,18 +546,18 @@ where
 impl<V, I, F> Interactive for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        &mut self.listeners
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        &mut self.interactive_state
     }
 }
 
 impl<V, I, F> Hover for Div<V, I, F>
 where
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
@@ -612,14 +571,14 @@ where
 
 impl<V, F> Click for Div<V, Identified, F>
 where
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
 }
 
 impl<V, F> Active for Div<V, Identified, F>
 where
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {

crates/gpui3/src/elements/img.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
-    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover,
-    Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
-    StyleRefinement, Styled, ViewContext,
+    ElementFocusability, ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover,
+    Identified, Interactive, InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels,
+    SharedString, StyleRefinement, Styled, ViewContext,
 };
 use futures::FutureExt;
 use util::ResultExt;
@@ -10,7 +10,7 @@ use util::ResultExt;
 pub struct Img<
     V: 'static + Send + Sync,
     I: ElementIdentity = Anonymous,
-    F: ElementFocusability = NonFocusable,
+    F: ElementFocusability<V> = NonFocusable,
 > {
     base: Div<V, I, F>,
     uri: Option<SharedString>,
@@ -32,7 +32,7 @@ impl<V, I, F> Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
         self.uri = Some(uri.into());
@@ -48,7 +48,7 @@ where
 impl<V, F> Img<V, Anonymous, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     pub fn id(self, id: impl Into<ElementId>) -> Img<V, Identified, F> {
         Img {
@@ -63,7 +63,7 @@ impl<V, I, F> IntoAnyElement<V> for Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
@@ -74,7 +74,7 @@ impl<V, I, F> Element for Img<V, I, F>
 where
     V: Send + Sync + 'static,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     type ViewState = V;
     type ElementState = DivState;
@@ -143,7 +143,7 @@ impl<V, I, F> Styled for Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn style(&mut self) -> &mut StyleRefinement {
         self.base.style()
@@ -154,10 +154,10 @@ impl<V, I, F> Interactive for Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        self.base.listeners()
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        self.base.interactive_state()
     }
 }
 
@@ -165,7 +165,7 @@ impl<V, I, F> Hover for Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_hover_style(group, style);
@@ -175,25 +175,29 @@ where
 impl<V, F> Click for Img<V, Identified, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
 }
 
 impl<V, F> Active for Img<V, Identified, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_active_style(group, style)
     }
 }
 
-impl<V, I> Focus for Img<V, I, Focusable>
+impl<V, I> Focus for Img<V, I, Focusable<V>>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
 {
+    fn focus_listeners(&mut self) -> &mut FocusListeners<Self::ViewState> {
+        self.base.focus_listeners()
+    }
+
     fn set_focus_style(&mut self, style: StyleRefinement) {
         self.base.set_focus_style(style)
     }

crates/gpui3/src/elements/svg.rs 🔗

@@ -1,15 +1,15 @@
 use crate::{
     div, Active, Anonymous, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability,
-    ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover, Identified, Interactive,
-    IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, StyleRefinement, Styled,
-    ViewContext,
+    ElementId, ElementIdentity, Focus, FocusListeners, Focusable, Hover, Identified, Interactive,
+    InteractiveState, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
+    StyleRefinement, Styled, ViewContext,
 };
 use util::ResultExt;
 
 pub struct Svg<
     V: 'static + Send + Sync,
     I: ElementIdentity = Anonymous,
-    F: ElementFocusability = NonFocusable,
+    F: ElementFocusability<V> = NonFocusable,
 > {
     base: Div<V, I, F>,
     path: Option<SharedString>,
@@ -29,7 +29,7 @@ impl<V, I, F> Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     pub fn path(mut self, path: impl Into<SharedString>) -> Self {
         self.path = Some(path.into());
@@ -40,7 +40,7 @@ where
 impl<V, F> Svg<V, Anonymous, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     pub fn id(self, id: impl Into<ElementId>) -> Svg<V, Identified, F> {
         Svg {
@@ -54,7 +54,7 @@ impl<V, I, F> IntoAnyElement<V> for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
@@ -65,7 +65,7 @@ impl<V, I, F> Element for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     type ViewState = V;
     type ElementState = DivState;
@@ -117,7 +117,7 @@ impl<V, I, F> Styled for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn style(&mut self) -> &mut StyleRefinement {
         self.base.style()
@@ -128,10 +128,10 @@ impl<V, I, F> Interactive for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
-    fn listeners(&mut self) -> &mut EventListeners<V> {
-        self.base.listeners()
+    fn interactive_state(&mut self) -> &mut InteractiveState<V> {
+        self.base.interactive_state()
     }
 }
 
@@ -139,7 +139,7 @@ impl<V, I, F> Hover for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_hover_style(group, style);
@@ -149,25 +149,29 @@ where
 impl<V, F> Click for Svg<V, Identified, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
 }
 
 impl<V, F> Active for Svg<V, Identified, F>
 where
     V: 'static + Send + Sync,
-    F: ElementFocusability,
+    F: ElementFocusability<V>,
 {
     fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
         self.base.set_active_style(group, style)
     }
 }
 
-impl<V, I> Focus for Svg<V, I, Focusable>
+impl<V, I> Focus for Svg<V, I, Focusable<V>>
 where
     V: 'static + Send + Sync,
     I: ElementIdentity,
 {
+    fn focus_listeners(&mut self) -> &mut FocusListeners<Self::ViewState> {
+        self.base.focus_listeners()
+    }
+
     fn set_focus_style(&mut self, style: StyleRefinement) {
         self.base.set_focus_style(style)
     }

crates/gpui3/src/events.rs 🔗

@@ -1,290 +0,0 @@
-use crate::{
-    point, Action, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point,
-    ViewContext,
-};
-use smallvec::SmallVec;
-use std::{
-    any::{Any, TypeId},
-    ops::Deref,
-    sync::Arc,
-};
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct KeyDownEvent {
-    pub keystroke: Keystroke,
-    pub is_held: bool,
-}
-
-#[derive(Clone, Debug)]
-pub struct KeyUpEvent {
-    pub keystroke: Keystroke,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct ModifiersChangedEvent {
-    pub modifiers: Modifiers,
-}
-
-impl Deref for ModifiersChangedEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-/// The phase of a touch motion event.
-/// Based on the winit enum of the same name.
-#[derive(Clone, Copy, Debug)]
-pub enum TouchPhase {
-    Started,
-    Moved,
-    Ended,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseDownEvent {
-    pub button: MouseButton,
-    pub position: Point<Pixels>,
-    pub modifiers: Modifiers,
-    pub click_count: usize,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseUpEvent {
-    pub button: MouseButton,
-    pub position: Point<Pixels>,
-    pub modifiers: Modifiers,
-    pub click_count: usize,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseClickEvent {
-    pub down: MouseDownEvent,
-    pub up: MouseUpEvent,
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum MouseButton {
-    Left,
-    Right,
-    Middle,
-    Navigate(NavigationDirection),
-}
-
-impl MouseButton {
-    pub fn all() -> Vec<Self> {
-        vec![
-            MouseButton::Left,
-            MouseButton::Right,
-            MouseButton::Middle,
-            MouseButton::Navigate(NavigationDirection::Back),
-            MouseButton::Navigate(NavigationDirection::Forward),
-        ]
-    }
-}
-
-impl Default for MouseButton {
-    fn default() -> Self {
-        Self::Left
-    }
-}
-
-#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
-pub enum NavigationDirection {
-    Back,
-    Forward,
-}
-
-impl Default for NavigationDirection {
-    fn default() -> Self {
-        Self::Back
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseMoveEvent {
-    pub position: Point<Pixels>,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-#[derive(Clone, Debug)]
-pub struct ScrollWheelEvent {
-    pub position: Point<Pixels>,
-    pub delta: ScrollDelta,
-    pub modifiers: Modifiers,
-    pub touch_phase: TouchPhase,
-}
-
-impl Deref for ScrollWheelEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum ScrollDelta {
-    Pixels(Point<Pixels>),
-    Lines(Point<f32>),
-}
-
-impl Default for ScrollDelta {
-    fn default() -> Self {
-        Self::Lines(Default::default())
-    }
-}
-
-impl ScrollDelta {
-    pub fn precise(&self) -> bool {
-        match self {
-            ScrollDelta::Pixels(_) => true,
-            ScrollDelta::Lines(_) => false,
-        }
-    }
-
-    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
-        match self {
-            ScrollDelta::Pixels(delta) => *delta,
-            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
-        }
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct MouseExitEvent {
-    pub position: Point<Pixels>,
-    pub pressed_button: Option<MouseButton>,
-    pub modifiers: Modifiers,
-}
-
-impl Deref for MouseExitEvent {
-    type Target = Modifiers;
-
-    fn deref(&self) -> &Self::Target {
-        &self.modifiers
-    }
-}
-
-#[derive(Clone, Debug)]
-pub enum InputEvent {
-    KeyDown(KeyDownEvent),
-    KeyUp(KeyUpEvent),
-    ModifiersChanged(ModifiersChangedEvent),
-    MouseDown(MouseDownEvent),
-    MouseUp(MouseUpEvent),
-    MouseMoved(MouseMoveEvent),
-    MouseExited(MouseExitEvent),
-    ScrollWheel(ScrollWheelEvent),
-}
-
-impl InputEvent {
-    pub fn position(&self) -> Option<Point<Pixels>> {
-        match self {
-            InputEvent::KeyDown { .. } => None,
-            InputEvent::KeyUp { .. } => None,
-            InputEvent::ModifiersChanged { .. } => None,
-            InputEvent::MouseDown(event) => Some(event.position),
-            InputEvent::MouseUp(event) => Some(event.position),
-            InputEvent::MouseMoved(event) => Some(event.position),
-            InputEvent::MouseExited(event) => Some(event.position),
-            InputEvent::ScrollWheel(event) => Some(event.position),
-        }
-    }
-
-    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
-        match self {
-            InputEvent::KeyDown { .. } => None,
-            InputEvent::KeyUp { .. } => None,
-            InputEvent::ModifiersChanged { .. } => None,
-            InputEvent::MouseDown(event) => Some(event),
-            InputEvent::MouseUp(event) => Some(event),
-            InputEvent::MouseMoved(event) => Some(event),
-            InputEvent::MouseExited(event) => Some(event),
-            InputEvent::ScrollWheel(event) => Some(event),
-        }
-    }
-
-    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
-        match self {
-            InputEvent::KeyDown(event) => Some(event),
-            InputEvent::KeyUp(event) => Some(event),
-            InputEvent::ModifiersChanged(event) => Some(event),
-            InputEvent::MouseDown(_) => None,
-            InputEvent::MouseUp(_) => None,
-            InputEvent::MouseMoved(_) => None,
-            InputEvent::MouseExited(_) => None,
-            InputEvent::ScrollWheel(_) => None,
-        }
-    }
-}
-
-pub struct FocusEvent {
-    pub blurred: Option<FocusHandle>,
-    pub focused: Option<FocusHandle>,
-}
-
-pub type MouseDownListener<V> = Arc<
-    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-pub type MouseUpListener<V> = Arc<
-    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-pub type MouseClickListener<V> =
-    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-
-pub type MouseMoveListener<V> = Arc<
-    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub type ScrollWheelListener<V> = Arc<
-    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub type KeyListener<V> = Arc<
-    dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) -> Option<Box<dyn Action>>
-        + Send
-        + Sync
-        + 'static,
->;
-
-pub type FocusListener<V> =
-    Arc<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
-
-pub struct EventListeners<V: 'static> {
-    pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
-    pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
-    pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
-    pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
-    pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
-    pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
-    pub focus: SmallVec<[FocusListener<V>; 2]>,
-}
-
-impl<V> Default for EventListeners<V> {
-    fn default() -> Self {
-        Self {
-            mouse_down: SmallVec::new(),
-            mouse_up: SmallVec::new(),
-            mouse_click: SmallVec::new(),
-            mouse_move: SmallVec::new(),
-            scroll_wheel: SmallVec::new(),
-            key: SmallVec::new(),
-            focus: SmallVec::new(),
-        }
-    }
-}

crates/gpui3/src/focus.rs 🔗

@@ -1,7 +1,14 @@
-use crate::{FocusEvent, FocusHandle, Interactive, StyleRefinement, ViewContext};
+use crate::{Element, FocusEvent, FocusHandle, StyleRefinement, ViewContext};
+use smallvec::SmallVec;
 use std::sync::Arc;
 
-pub trait Focus: Interactive {
+pub type FocusListeners<V> = SmallVec<[FocusListener<V>; 2]>;
+
+pub type FocusListener<V> =
+    Arc<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub trait Focus: Element {
+    fn focus_listeners(&mut self) -> &mut FocusListeners<Self::ViewState>;
     fn set_focus_style(&mut self, style: StyleRefinement);
     fn set_focus_in_style(&mut self, style: StyleRefinement);
     fn set_in_focus_style(&mut self, style: StyleRefinement);
@@ -42,8 +49,7 @@ pub trait Focus: Interactive {
         Self: Sized,
     {
         let handle = self.handle().clone();
-        self.listeners()
-            .focus
+        self.focus_listeners()
             .push(Arc::new(move |view, event, cx| {
                 if event.focused.as_ref() == Some(&handle) {
                     listener(view, event, cx)
@@ -63,8 +69,7 @@ pub trait Focus: Interactive {
         Self: Sized,
     {
         let handle = self.handle().clone();
-        self.listeners()
-            .focus
+        self.focus_listeners()
             .push(Arc::new(move |view, event, cx| {
                 if event.blurred.as_ref() == Some(&handle) {
                     listener(view, event, cx)
@@ -84,8 +89,7 @@ pub trait Focus: Interactive {
         Self: Sized,
     {
         let handle = self.handle().clone();
-        self.listeners()
-            .focus
+        self.focus_listeners()
             .push(Arc::new(move |view, event, cx| {
                 let descendant_blurred = event
                     .blurred
@@ -114,8 +118,7 @@ pub trait Focus: Interactive {
         Self: Sized,
     {
         let handle = self.handle().clone();
-        self.listeners()
-            .focus
+        self.focus_listeners()
             .push(Arc::new(move |view, event, cx| {
                 let descendant_blurred = event
                     .blurred

crates/gpui3/src/gpui3.rs 🔗

@@ -5,7 +5,6 @@ mod assets;
 mod color;
 mod element;
 mod elements;
-mod events;
 mod executor;
 mod focus;
 mod geometry;
@@ -33,7 +32,6 @@ pub use assets::*;
 pub use color::*;
 pub use element::*;
 pub use elements::*;
-pub use events::*;
 pub use executor::*;
 pub use focus::*;
 pub use geometry::*;

crates/gpui3/src/interactive.rs 🔗

@@ -1,12 +1,17 @@
-use std::{any::TypeId, sync::Arc};
+use smallvec::SmallVec;
 
 use crate::{
-    DispatchPhase, Element, EventListeners, KeyDownEvent, KeyUpEvent, MouseButton, MouseClickEvent,
-    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
+    point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke,
+    Modifiers, Pixels, Point, ViewContext,
+};
+use std::{
+    any::{Any, TypeId},
+    ops::Deref,
+    sync::Arc,
 };
 
 pub trait Interactive: Element {
-    fn listeners(&mut self) -> &mut EventListeners<Self::ViewState>;
+    fn interactive_state(&mut self) -> &mut InteractiveState<Self::ViewState>;
 
     fn on_mouse_down(
         mut self,
@@ -19,16 +24,16 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_down.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
                     && bounds.contains_point(&event.position)
                 {
                     handler(view, event, cx)
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -43,7 +48,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_up
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
@@ -67,16 +72,16 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_down
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_down.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
                     && !bounds.contains_point(&event.position)
                 {
                     handler(view, event, cx)
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -91,7 +96,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_up
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
@@ -114,13 +119,13 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .mouse_move
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().mouse_move.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -134,13 +139,13 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners()
-            .scroll_wheel
-            .push(Arc::new(move |view, event, bounds, phase, cx| {
+        self.interactive_state().scroll_wheel.push(Arc::new(
+            move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
                 }
-            }));
+            },
+        ));
         self
     }
 
@@ -158,9 +163,9 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<KeyDownEvent>(),
-            Arc::new(move |view, event, phase, cx| {
+            Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
                 listener(view, event, phase, cx);
                 None
@@ -179,9 +184,9 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<KeyUpEvent>(),
-            Arc::new(move |view, event, phase, cx| {
+            Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
                 listener(view, event, phase, cx);
                 None
@@ -200,9 +205,9 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.listeners().key.push((
+        self.interactive_state().key.push((
             TypeId::of::<A>(),
-            Arc::new(move |view, event, phase, cx| {
+            Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
                 listener(view, event, phase, cx);
                 None
@@ -223,9 +228,290 @@ pub trait Click: Interactive {
     where
         Self: Sized,
     {
-        self.listeners()
+        self.interactive_state()
             .mouse_click
             .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
         self
     }
 }
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct KeyDownEvent {
+    pub keystroke: Keystroke,
+    pub is_held: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct KeyUpEvent {
+    pub keystroke: Keystroke,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct ModifiersChangedEvent {
+    pub modifiers: Modifiers,
+}
+
+impl Deref for ModifiersChangedEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+/// The phase of a touch motion event.
+/// Based on the winit enum of the same name.
+#[derive(Clone, Copy, Debug)]
+pub enum TouchPhase {
+    Started,
+    Moved,
+    Ended,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseDownEvent {
+    pub button: MouseButton,
+    pub position: Point<Pixels>,
+    pub modifiers: Modifiers,
+    pub click_count: usize,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseUpEvent {
+    pub button: MouseButton,
+    pub position: Point<Pixels>,
+    pub modifiers: Modifiers,
+    pub click_count: usize,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseClickEvent {
+    pub down: MouseDownEvent,
+    pub up: MouseUpEvent,
+}
+
+#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
+pub enum MouseButton {
+    Left,
+    Right,
+    Middle,
+    Navigate(NavigationDirection),
+}
+
+impl MouseButton {
+    pub fn all() -> Vec<Self> {
+        vec![
+            MouseButton::Left,
+            MouseButton::Right,
+            MouseButton::Middle,
+            MouseButton::Navigate(NavigationDirection::Back),
+            MouseButton::Navigate(NavigationDirection::Forward),
+        ]
+    }
+}
+
+impl Default for MouseButton {
+    fn default() -> Self {
+        Self::Left
+    }
+}
+
+#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
+pub enum NavigationDirection {
+    Back,
+    Forward,
+}
+
+impl Default for NavigationDirection {
+    fn default() -> Self {
+        Self::Back
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseMoveEvent {
+    pub position: Point<Pixels>,
+    pub pressed_button: Option<MouseButton>,
+    pub modifiers: Modifiers,
+}
+
+#[derive(Clone, Debug)]
+pub struct ScrollWheelEvent {
+    pub position: Point<Pixels>,
+    pub delta: ScrollDelta,
+    pub modifiers: Modifiers,
+    pub touch_phase: TouchPhase,
+}
+
+impl Deref for ScrollWheelEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum ScrollDelta {
+    Pixels(Point<Pixels>),
+    Lines(Point<f32>),
+}
+
+impl Default for ScrollDelta {
+    fn default() -> Self {
+        Self::Lines(Default::default())
+    }
+}
+
+impl ScrollDelta {
+    pub fn precise(&self) -> bool {
+        match self {
+            ScrollDelta::Pixels(_) => true,
+            ScrollDelta::Lines(_) => false,
+        }
+    }
+
+    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
+        match self {
+            ScrollDelta::Pixels(delta) => *delta,
+            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
+        }
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MouseExitEvent {
+    pub position: Point<Pixels>,
+    pub pressed_button: Option<MouseButton>,
+    pub modifiers: Modifiers,
+}
+
+impl Deref for MouseExitEvent {
+    type Target = Modifiers;
+
+    fn deref(&self) -> &Self::Target {
+        &self.modifiers
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum InputEvent {
+    KeyDown(KeyDownEvent),
+    KeyUp(KeyUpEvent),
+    ModifiersChanged(ModifiersChangedEvent),
+    MouseDown(MouseDownEvent),
+    MouseUp(MouseUpEvent),
+    MouseMoved(MouseMoveEvent),
+    MouseExited(MouseExitEvent),
+    ScrollWheel(ScrollWheelEvent),
+}
+
+impl InputEvent {
+    pub fn position(&self) -> Option<Point<Pixels>> {
+        match self {
+            InputEvent::KeyDown { .. } => None,
+            InputEvent::KeyUp { .. } => None,
+            InputEvent::ModifiersChanged { .. } => None,
+            InputEvent::MouseDown(event) => Some(event.position),
+            InputEvent::MouseUp(event) => Some(event.position),
+            InputEvent::MouseMoved(event) => Some(event.position),
+            InputEvent::MouseExited(event) => Some(event.position),
+            InputEvent::ScrollWheel(event) => Some(event.position),
+        }
+    }
+
+    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
+        match self {
+            InputEvent::KeyDown { .. } => None,
+            InputEvent::KeyUp { .. } => None,
+            InputEvent::ModifiersChanged { .. } => None,
+            InputEvent::MouseDown(event) => Some(event),
+            InputEvent::MouseUp(event) => Some(event),
+            InputEvent::MouseMoved(event) => Some(event),
+            InputEvent::MouseExited(event) => Some(event),
+            InputEvent::ScrollWheel(event) => Some(event),
+        }
+    }
+
+    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
+        match self {
+            InputEvent::KeyDown(event) => Some(event),
+            InputEvent::KeyUp(event) => Some(event),
+            InputEvent::ModifiersChanged(event) => Some(event),
+            InputEvent::MouseDown(_) => None,
+            InputEvent::MouseUp(_) => None,
+            InputEvent::MouseMoved(_) => None,
+            InputEvent::MouseExited(_) => None,
+            InputEvent::ScrollWheel(_) => None,
+        }
+    }
+}
+
+pub struct FocusEvent {
+    pub blurred: Option<FocusHandle>,
+    pub focused: Option<FocusHandle>,
+}
+
+pub type MouseDownListener<V> = Arc<
+    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseUpListener<V> = Arc<
+    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+pub type MouseClickListener<V> =
+    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
+
+pub type MouseMoveListener<V> = Arc<
+    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type ScrollWheelListener<V> = Arc<
+    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub type KeyListener<V> = Arc<
+    dyn Fn(
+            &mut V,
+            &dyn Any,
+            &[&DispatchContext],
+            DispatchPhase,
+            &mut ViewContext<V>,
+        ) -> Option<Box<dyn Action>>
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub struct InteractiveState<V: 'static> {
+    pub mouse_down: SmallVec<[MouseDownListener<V>; 2]>,
+    pub mouse_up: SmallVec<[MouseUpListener<V>; 2]>,
+    pub mouse_click: SmallVec<[MouseClickListener<V>; 2]>,
+    pub mouse_move: SmallVec<[MouseMoveListener<V>; 2]>,
+    pub scroll_wheel: SmallVec<[ScrollWheelListener<V>; 2]>,
+    pub key: SmallVec<[(TypeId, KeyListener<V>); 32]>,
+}
+
+impl<V> Default for InteractiveState<V> {
+    fn default() -> Self {
+        Self {
+            mouse_down: SmallVec::new(),
+            mouse_up: SmallVec::new(),
+            mouse_click: SmallVec::new(),
+            mouse_move: SmallVec::new(),
+            scroll_wheel: SmallVec::new(),
+            key: SmallVec::new(),
+        }
+    }
+}

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

@@ -1,4 +1,4 @@
-use crate::{Action, ActionContext, ActionContextPredicate, KeyMatch, Keystroke};
+use crate::{Action, ActionContextPredicate, DispatchContext, KeyMatch, Keystroke};
 use anyhow::Result;
 use smallvec::SmallVec;
 
@@ -32,7 +32,7 @@ impl KeyBinding {
         })
     }
 
-    pub fn matches_context(&self, contexts: &[ActionContext]) -> bool {
+    pub fn matches_context(&self, contexts: &[&DispatchContext]) -> bool {
         self.context_predicate
             .as_ref()
             .map(|predicate| predicate.eval(contexts))
@@ -42,7 +42,7 @@ impl KeyBinding {
     pub fn match_keystrokes(
         &self,
         pending_keystrokes: &[Keystroke],
-        contexts: &[ActionContext],
+        contexts: &[&DispatchContext],
     ) -> KeyMatch {
         if self.keystrokes.as_ref().starts_with(&pending_keystrokes)
             && self.matches_context(contexts)
@@ -61,7 +61,7 @@ impl KeyBinding {
     pub fn keystrokes_for_action(
         &self,
         action: &dyn Action,
-        contexts: &[ActionContext],
+        contexts: &[&DispatchContext],
     ) -> Option<SmallVec<[Keystroke; 2]>> {
         if self.action.eq(action) && self.matches_context(contexts) {
             Some(self.keystrokes.clone())

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

@@ -1,4 +1,4 @@
-use crate::{Action, ActionContext, Keymap, KeymapVersion, Keystroke};
+use crate::{Action, DispatchContext, Keymap, KeymapVersion, Keystroke};
 use parking_lot::RwLock;
 use smallvec::SmallVec;
 use std::sync::Arc;
@@ -44,7 +44,7 @@ impl KeyMatcher {
     pub fn match_keystroke(
         &mut self,
         keystroke: &Keystroke,
-        context_stack: &[ActionContext],
+        context_stack: &[&DispatchContext],
     ) -> KeyMatch {
         let keymap = self.keymap.read();
         // Clear pending keystrokes if the keymap has changed since the last matched keystroke.
@@ -86,7 +86,7 @@ impl KeyMatcher {
     pub fn keystrokes_for_action(
         &self,
         action: &dyn Action,
-        contexts: &[ActionContext],
+        contexts: &[&DispatchContext],
     ) -> Option<SmallVec<[Keystroke; 2]>> {
         self.keymap
             .read()

crates/gpui3/src/window.rs 🔗

@@ -1,13 +1,13 @@
 use crate::{
     px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
-    BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, Edges, Effect,
-    Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla,
-    ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
-    MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, Platform,
-    PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
-    RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
-    Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
-    WindowOptions, SUBPIXEL_VARIANTS,
+    BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext,
+    DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId,
+    GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher,
+    Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path,
+    Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference,
+    RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow,
+    SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
+    WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use collections::HashMap;
@@ -47,7 +47,12 @@ pub enum DispatchPhase {
 
 type AnyListener = Arc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
 type AnyKeyListener = Arc<
-    dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) -> Option<Box<dyn Action>>
+    dyn Fn(
+            &dyn Any,
+            &[&DispatchContext],
+            DispatchPhase,
+            &mut WindowContext,
+        ) -> Option<Box<dyn Action>>
         + Send
         + Sync
         + 'static,
@@ -155,8 +160,8 @@ pub struct Window {
     z_index_stack: StackingOrder,
     content_mask_stack: Vec<ContentMask<Pixels>>,
     mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
-    key_listeners: Vec<(TypeId, AnyKeyListener)>,
-    key_events_enabled: bool,
+    key_dispatch_stack: Vec<KeyDispatchStackFrame>,
+    freeze_key_dispatch_stack: bool,
     focus_stack: Vec<FocusId>,
     focus_parents_by_child: HashMap<FocusId, FocusId>,
     pub(crate) focus_listeners: Vec<AnyFocusListener>,
@@ -230,24 +235,32 @@ impl Window {
             z_index_stack: StackingOrder(SmallVec::new()),
             content_mask_stack: Vec::new(),
             mouse_listeners: HashMap::default(),
-            key_listeners: Vec::new(),
-            key_events_enabled: true,
+            key_dispatch_stack: Vec::new(),
+            freeze_key_dispatch_stack: false,
             focus_stack: Vec::new(),
             focus_parents_by_child: HashMap::default(),
             focus_listeners: Vec::new(),
+            focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
             propagate: true,
             default_prevented: true,
             mouse_position,
             scale_factor,
             scene_builder: SceneBuilder::new(),
             dirty: true,
-            focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
             last_blur: None,
             focus: None,
         }
     }
 }
 
+enum KeyDispatchStackFrame {
+    Listener {
+        event_type: TypeId,
+        listener: AnyKeyListener,
+    },
+    Context(DispatchContext),
+}
+
 #[derive(Clone, Debug, Default, PartialEq, Eq)]
 #[repr(C)]
 pub struct ContentMask<P: Clone + Default + Debug> {
@@ -833,9 +846,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         // Clear focus state, because we determine what is focused when the new elements
         // in the upcoming frame are initialized.
         window.focus_listeners.clear();
-        window.key_listeners.clear();
+        window.key_dispatch_stack.clear();
         window.focus_parents_by_child.clear();
-        window.key_events_enabled = true;
+        window.freeze_key_dispatch_stack = false;
     }
 
     fn dispatch_event(&mut self, event: InputEvent) -> bool {
@@ -888,36 +901,67 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                     .insert(any_mouse_event.type_id(), handlers);
             }
         } else if let Some(any_key_event) = event.keyboard_event() {
-            let key_listeners = mem::take(&mut self.window.key_listeners);
+            let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack);
             let key_event_type = any_key_event.type_id();
-
-            for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate() {
-                if key_event_type == *listener_event_type {
-                    if let Some(action) = listener(any_key_event, DispatchPhase::Capture, self) {
-                        self.dispatch_action(action, &key_listeners[..ix]);
+            let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new();
+
+            for (ix, frame) in key_dispatch_stack.iter().enumerate() {
+                match frame {
+                    KeyDispatchStackFrame::Listener {
+                        event_type,
+                        listener,
+                    } => {
+                        if key_event_type == *event_type {
+                            if let Some(action) = listener(
+                                any_key_event,
+                                &context_stack,
+                                DispatchPhase::Capture,
+                                self,
+                            ) {
+                                self.dispatch_action(action, &key_dispatch_stack[..ix]);
+                            }
+                            if !self.window.propagate {
+                                break;
+                            }
+                        }
                     }
-                    if !self.window.propagate {
-                        break;
+                    KeyDispatchStackFrame::Context(context) => {
+                        context_stack.push(&context);
                     }
                 }
             }
 
             if self.window.propagate {
-                for (ix, (listener_event_type, listener)) in key_listeners.iter().enumerate().rev()
-                {
-                    if key_event_type == *listener_event_type {
-                        if let Some(action) = listener(any_key_event, DispatchPhase::Bubble, self) {
-                            self.dispatch_action(action, &key_listeners[..ix]);
-                        }
+                for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() {
+                    match frame {
+                        KeyDispatchStackFrame::Listener {
+                            event_type,
+                            listener,
+                        } => {
+                            if key_event_type == *event_type {
+                                if let Some(action) = listener(
+                                    any_key_event,
+                                    &context_stack,
+                                    DispatchPhase::Bubble,
+                                    self,
+                                ) {
+                                    self.dispatch_action(action, &key_dispatch_stack[..ix]);
+                                }
 
-                        if !self.window.propagate {
-                            break;
+                                if !self.window.propagate {
+                                    break;
+                                }
+                            }
+                        }
+                        KeyDispatchStackFrame::Context(_) => {
+                            context_stack.pop();
                         }
                     }
                 }
             }
 
-            self.window.key_listeners = key_listeners;
+            drop(context_stack);
+            self.window.key_dispatch_stack = key_dispatch_stack;
         }
 
         true
@@ -927,13 +971,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         &mut self,
         element_id: &GlobalElementId,
         keystroke: &Keystroke,
+        context_stack: &[&DispatchContext],
     ) -> KeyMatch {
         let key_match = self
             .window
             .key_matchers
             .get_mut(element_id)
             .unwrap()
-            .match_keystroke(keystroke, &[]);
+            .match_keystroke(keystroke, context_stack);
 
         if key_match.is_some() {
             for matcher in self.window.key_matchers.values_mut() {
@@ -944,23 +989,39 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         key_match
     }
 
-    fn dispatch_action(&mut self, action: Box<dyn Action>, listeners: &[(TypeId, AnyKeyListener)]) {
+    fn dispatch_action(
+        &mut self,
+        action: Box<dyn Action>,
+        dispatch_stack: &[KeyDispatchStackFrame],
+    ) {
         let action_type = action.as_any().type_id();
-        for (event_type, listener) in listeners {
-            if action_type == *event_type {
-                listener(action.as_any(), DispatchPhase::Capture, self);
-                if !self.window.propagate {
-                    break;
+        for stack_frame in dispatch_stack {
+            if let KeyDispatchStackFrame::Listener {
+                event_type,
+                listener,
+            } = stack_frame
+            {
+                if action_type == *event_type {
+                    listener(action.as_any(), &[], DispatchPhase::Capture, self);
+                    if !self.window.propagate {
+                        break;
+                    }
                 }
             }
         }
 
         if self.window.propagate {
-            for (event_type, listener) in listeners.iter().rev() {
-                if action_type == *event_type {
-                    listener(action.as_any(), DispatchPhase::Bubble, self);
-                    if !self.window.propagate {
-                        break;
+            for stack_frame in dispatch_stack.iter().rev() {
+                if let KeyDispatchStackFrame::Listener {
+                    event_type,
+                    listener,
+                } = stack_frame
+                {
+                    if action_type == *event_type {
+                        listener(action.as_any(), &[], DispatchPhase::Bubble, self);
+                        if !self.window.propagate {
+                            break;
+                        }
                     }
                 }
             }
@@ -1287,26 +1348,56 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         key_listeners: &[(TypeId, KeyListener<V>)],
         f: impl FnOnce(&mut Self) -> R,
     ) -> R {
-        if self.window.key_events_enabled {
+        if !self.window.freeze_key_dispatch_stack {
             for (event_type, listener) in key_listeners.iter().cloned() {
                 let handle = self.handle();
                 let listener = Arc::new(
-                    move |event: &dyn Any, phase: DispatchPhase, cx: &mut WindowContext<'_, '_>| {
+                    move |event: &dyn Any,
+                          context_stack: &[&DispatchContext],
+                          phase: DispatchPhase,
+                          cx: &mut WindowContext<'_, '_>| {
                         handle
-                            .update(cx, |view, cx| listener(view, event, phase, cx))
+                            .update(cx, |view, cx| {
+                                listener(view, event, context_stack, phase, cx)
+                            })
                             .log_err()
                             .flatten()
                     },
                 );
-                self.window.key_listeners.push((event_type, listener));
+                self.window
+                    .key_dispatch_stack
+                    .push(KeyDispatchStackFrame::Listener {
+                        event_type,
+                        listener,
+                    });
             }
         }
 
         let result = f(self);
 
-        if self.window.key_events_enabled {
-            let prev_len = self.window.key_listeners.len() - key_listeners.len();
-            self.window.key_listeners.truncate(prev_len);
+        if !self.window.freeze_key_dispatch_stack {
+            let prev_len = self.window.key_dispatch_stack.len() - key_listeners.len();
+            self.window.key_dispatch_stack.truncate(prev_len);
+        }
+
+        result
+    }
+
+    pub fn with_key_dispatch_context<R>(
+        &mut self,
+        context: DispatchContext,
+        f: impl FnOnce(&mut Self) -> R,
+    ) -> R {
+        if !self.window.freeze_key_dispatch_stack {
+            self.window
+                .key_dispatch_stack
+                .push(KeyDispatchStackFrame::Context(context));
+        }
+
+        let result = f(self);
+
+        if !self.window.freeze_key_dispatch_stack {
+            self.window.key_dispatch_stack.pop();
         }
 
         result
@@ -1325,7 +1416,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         self.window.focus_stack.push(focus_handle.id);
 
         if Some(focus_handle.id) == self.window.focus {
-            self.window.key_events_enabled = false;
+            self.window.freeze_key_dispatch_stack = true;
         }
 
         let result = f(self);