Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/elements/div.rs |  32 +++++-
crates/gpui3/src/events.rs       |  11 +
crates/gpui3/src/focus.rs        |  52 ----------
crates/gpui3/src/gpui3.rs        |   4 
crates/gpui3/src/interactive.rs  |  73 +++++++++++++++
crates/gpui3/src/keymap.rs       |  16 +++
crates/gpui3/src/window.rs       | 156 +++++++++++++++++-----------------
7 files changed, 203 insertions(+), 141 deletions(-)

Detailed changes

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

@@ -1,15 +1,16 @@
 use crate::{
     Active, Anonymous, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
     ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, FocusHandle, Focusable,
-    Hover, Identified, Interactive, IntoAnyElement, LayoutId, MouseClickEvent, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point,
-    ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext,
+    Hover, Identified, Interactive, 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;
 use refineable::Refineable;
 use smallvec::SmallVec;
-use std::{mem, sync::Arc};
+use std::{any::TypeId, mem, sync::Arc};
 
 #[derive(Default)]
 pub struct DivState {
@@ -423,11 +424,30 @@ where
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
     ) -> Self::ElementState {
+        let element_state = element_state.unwrap_or_default();
         for listener in self.listeners.focus.iter().cloned() {
             cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
         }
 
-        let key_listeners = mem::take(&mut self.listeners.key);
+        let mut key_listeners = mem::take(&mut self.listeners.key);
+
+        if let Some(id) = self.id() {
+            key_listeners.push((
+                TypeId::of::<KeyDownEvent>(),
+                Arc::new(move |_, key_down, phase, cx| {
+                    if phase == DispatchPhase::Bubble {
+                        let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
+                        if let KeyMatch::Some(action) = cx.match_keystroke(&id, &key_down.keystroke)
+                        {
+                            return Some(action);
+                        }
+                    }
+
+                    None
+                }),
+            ));
+        }
+
         cx.with_key_listeners(&key_listeners, |cx| {
             if let Some(focus_handle) = self.focusability.focus_handle().cloned() {
                 cx.with_focus(focus_handle, |cx| {
@@ -443,7 +463,7 @@ where
         });
         self.listeners.key = key_listeners;
 
-        element_state.unwrap_or_default()
+        element_state
     }
 
     fn layout(

crates/gpui3/src/events.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
-    point, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point, ViewContext,
+    point, AnyBox, Bounds, DispatchPhase, FocusHandle, Keystroke, Modifiers, Pixels, Point,
+    ViewContext,
 };
 use smallvec::SmallVec;
 use std::{
@@ -254,8 +255,12 @@ pub type ScrollWheelListener<V> = Arc<
         + 'static,
 >;
 
-pub type KeyListener<V> =
-    Arc<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
+pub type KeyListener<V> = Arc<
+    dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) -> Option<AnyBox>
+        + Send
+        + Sync
+        + 'static,
+>;
 
 pub type FocusListener<V> =
     Arc<dyn Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + Send + Sync + 'static>;

crates/gpui3/src/focus.rs 🔗

@@ -1,9 +1,5 @@
-use std::{any::TypeId, sync::Arc};
-
-use crate::{
-    DispatchPhase, FocusEvent, FocusHandle, Interactive, KeyDownEvent, KeyUpEvent, StyleRefinement,
-    ViewContext,
-};
+use crate::{FocusEvent, FocusHandle, Interactive, StyleRefinement, ViewContext};
+use std::sync::Arc;
 
 pub trait Focus: Interactive {
     fn set_focus_style(&mut self, style: StyleRefinement);
@@ -135,48 +131,4 @@ pub trait Focus: Interactive {
             }));
         self
     }
-
-    fn on_key_down(
-        mut self,
-        listener: impl Fn(
-                &mut Self::ViewState,
-                &KeyDownEvent,
-                DispatchPhase,
-                &mut ViewContext<Self::ViewState>,
-            ) + Send
-            + Sync
-            + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.listeners().key.push((
-            TypeId::of::<KeyDownEvent>(),
-            Arc::new(move |view, event, phase, cx| {
-                let event = event.downcast_ref().unwrap();
-                listener(view, event, phase, cx)
-            }),
-        ));
-        self
-    }
-
-    fn on_key_up(
-        mut self,
-        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
-            + Send
-            + Sync
-            + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.listeners().key.push((
-            TypeId::of::<KeyUpEvent>(),
-            Arc::new(move |view, event, phase, cx| {
-                let event = event.downcast_ref().unwrap();
-                listener(view, event, phase, cx)
-            }),
-        ));
-        self
-    }
 }

crates/gpui3/src/gpui3.rs 🔗

@@ -11,6 +11,7 @@ mod geometry;
 mod hover;
 mod image_cache;
 mod interactive;
+mod keymap;
 mod platform;
 mod scene;
 mod style;
@@ -38,6 +39,7 @@ pub use gpui3_macros::*;
 pub use hover::*;
 pub use image_cache::*;
 pub use interactive::*;
+pub use keymap::*;
 pub use platform::*;
 pub use refineable::*;
 pub use scene::*;
@@ -64,7 +66,7 @@ use std::{
 };
 use taffy::TaffyLayoutEngine;
 
-type AnyBox = Box<dyn Any + Send + Sync + 'static>;
+type AnyBox = Box<dyn Any + Send + Sync>;
 
 pub trait Context {
     type EntityContext<'a, 'w, T: 'static + Send + Sync>;

crates/gpui3/src/interactive.rs 🔗

@@ -1,8 +1,8 @@
-use std::sync::Arc;
+use std::{any::TypeId, sync::Arc};
 
 use crate::{
-    DispatchPhase, Element, EventListeners, MouseButton, MouseClickEvent, MouseDownEvent,
-    MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
+    DispatchPhase, Element, EventListeners, KeyDownEvent, KeyUpEvent, MouseButton, MouseClickEvent,
+    MouseDownEvent, MouseMoveEvent, MouseUpEvent, ScrollWheelEvent, ViewContext,
 };
 
 pub trait Interactive: Element {
@@ -143,6 +143,73 @@ pub trait Interactive: Element {
             }));
         self
     }
+
+    fn on_key_down(
+        mut self,
+        listener: impl Fn(
+                &mut Self::ViewState,
+                &KeyDownEvent,
+                DispatchPhase,
+                &mut ViewContext<Self::ViewState>,
+            ) + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.listeners().key.push((
+            TypeId::of::<KeyDownEvent>(),
+            Arc::new(move |view, event, phase, cx| {
+                let event = event.downcast_ref().unwrap();
+                listener(view, event, phase, cx);
+                None
+            }),
+        ));
+        self
+    }
+
+    fn on_key_up(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.listeners().key.push((
+            TypeId::of::<KeyUpEvent>(),
+            Arc::new(move |view, event, phase, cx| {
+                let event = event.downcast_ref().unwrap();
+                listener(view, event, phase, cx);
+                None
+            }),
+        ));
+        self
+    }
+
+    fn on_action<A: 'static>(
+        mut self,
+        listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
+            + Send
+            + Sync
+            + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.listeners().key.push((
+            TypeId::of::<A>(),
+            Arc::new(move |view, event, phase, cx| {
+                let event = event.downcast_ref().unwrap();
+                listener(view, event, phase, cx);
+                None
+            }),
+        ));
+        self
+    }
 }
 
 pub trait Click: Interactive {

crates/gpui3/src/keymap.rs 🔗

@@ -0,0 +1,16 @@
+use crate::{AnyBox, Keystroke};
+
+#[derive(Default)]
+pub struct KeyMatcher;
+
+impl KeyMatcher {
+    pub fn push_keystroke(&mut self, keystroke: Keystroke) -> KeyMatch {
+        todo!()
+    }
+}
+
+pub enum KeyMatch {
+    None,
+    Pending,
+    Some(AnyBox),
+}

crates/gpui3/src/window.rs 🔗

@@ -2,11 +2,11 @@ use crate::{
     px, size, 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, 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,
+    InputEvent, IsZero, KeyListener, KeyMatch, 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;
@@ -45,6 +45,9 @@ 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<AnyBox> + Send + Sync + 'static,
+>;
 type AnyFocusListener = Arc<dyn Fn(&FocusEvent, &mut WindowContext) + Send + Sync + 'static>;
 
 slotmap::new_key_type! { pub struct FocusId; }
@@ -146,13 +149,13 @@ pub struct Window {
     z_index_stack: StackingOrder,
     content_mask_stack: Vec<ContentMask<Pixels>>,
     mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyListener)>>,
-    key_listeners: HashMap<TypeId, Vec<AnyListener>>,
+    key_listeners: Vec<(TypeId, AnyKeyListener)>,
     key_events_enabled: bool,
     focus_stack: Vec<FocusId>,
     focus_parents_by_child: HashMap<FocusId, FocusId>,
     pub(crate) focus_listeners: Vec<AnyFocusListener>,
     pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
-    propagate_event: bool,
+    propagate: bool,
     default_prevented: bool,
     mouse_position: Point<Pixels>,
     scale_factor: f32,
@@ -219,12 +222,12 @@ impl Window {
             z_index_stack: StackingOrder(SmallVec::new()),
             content_mask_stack: Vec::new(),
             mouse_listeners: HashMap::default(),
-            key_listeners: HashMap::default(),
+            key_listeners: Vec::new(),
             key_events_enabled: true,
             focus_stack: Vec::new(),
             focus_parents_by_child: HashMap::default(),
             focus_listeners: Vec::new(),
-            propagate_event: true,
+            propagate: true,
             default_prevented: true,
             mouse_position,
             scale_factor,
@@ -434,7 +437,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn stop_propagation(&mut self) {
-        self.window.propagate_event = false;
+        self.window.propagate = false;
     }
 
     pub fn prevent_default(&mut self) {
@@ -462,19 +465,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             ))
     }
 
-    pub fn on_keyboard_event<Event: 'static>(
-        &mut self,
-        handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static,
-    ) {
-        self.window
-            .key_listeners
-            .entry(TypeId::of::<Event>())
-            .or_default()
-            .push(Arc::new(move |event: &dyn Any, phase, cx| {
-                handler(event.downcast_ref().unwrap(), phase, cx)
-            }))
-    }
-
     pub fn mouse_position(&self) -> Point<Pixels> {
         self.window.mouse_position
     }
@@ -821,7 +811,7 @@ 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.values_mut().for_each(Vec::clear);
+        window.key_listeners.clear();
         window.focus_parents_by_child.clear();
         window.key_events_enabled = true;
     }
@@ -837,7 +827,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             }
 
             // Handlers may set this to false by calling `stop_propagation`
-            self.window.propagate_event = true;
+            self.window.propagate = true;
             self.window.default_prevented = false;
 
             if let Some(mut handlers) = self
@@ -852,16 +842,16 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 // special purposes, such as detecting events outside of a given Bounds.
                 for (_, handler) in &handlers {
                     handler(any_mouse_event, DispatchPhase::Capture, self);
-                    if !self.window.propagate_event {
+                    if !self.window.propagate {
                         break;
                     }
                 }
 
                 // Bubble phase, where most normal handlers do their work.
-                if self.window.propagate_event {
+                if self.window.propagate {
                     for (_, handler) in handlers.iter().rev() {
                         handler(any_mouse_event, DispatchPhase::Bubble, self);
-                        if !self.window.propagate_event {
+                        if !self.window.propagate {
                             break;
                         }
                     }
@@ -879,43 +869,68 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                     .mouse_listeners
                     .insert(any_mouse_event.type_id(), handlers);
             }
-        } else if let Some(any_keyboard_event) = event.keyboard_event() {
-            if let Some(mut handlers) = self
-                .window
-                .key_listeners
-                .remove(&any_keyboard_event.type_id())
-            {
-                for handler in &handlers {
-                    handler(any_keyboard_event, DispatchPhase::Capture, self);
-                    if !self.window.propagate_event {
+        } else if let Some(any_key_event) = event.keyboard_event() {
+            let key_listeners = mem::take(&mut self.window.key_listeners);
+            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]);
+                    }
+                    if !self.window.propagate {
                         break;
                     }
                 }
+            }
 
-                if self.window.propagate_event {
-                    for handler in handlers.iter().rev() {
-                        handler(any_keyboard_event, DispatchPhase::Bubble, self);
-                        if !self.window.propagate_event {
+            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]);
+                        }
+
+                        if !self.window.propagate {
                             break;
                         }
                     }
                 }
-
-                handlers.extend(
-                    self.window
-                        .key_listeners
-                        .get_mut(&any_keyboard_event.type_id())
-                        .into_iter()
-                        .flat_map(|handlers| handlers.drain(..)),
-                );
-                self.window
-                    .key_listeners
-                    .insert(any_keyboard_event.type_id(), handlers);
             }
+
+            self.window.key_listeners = key_listeners;
         }
 
         true
     }
+
+    pub fn match_keystroke(&mut self, element_id: &ElementId, keystroke: &Keystroke) -> KeyMatch {
+        todo!();
+    }
+
+    fn dispatch_action(&mut self, action: AnyBox, listeners: &[(TypeId, AnyKeyListener)]) {
+        let action_type = action.type_id();
+        for (event_type, listener) in listeners {
+            if action_type == *event_type {
+                listener(action.as_ref(), 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_ref(), DispatchPhase::Capture, self);
+                    if !self.window.propagate {
+                        break;
+                    }
+                }
+            }
+        }
+    }
 }
 
 impl<'a, 'w> MainThread<WindowContext<'a, 'w>> {
@@ -1225,28 +1240,25 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
         f: impl FnOnce(&mut Self) -> R,
     ) -> R {
         if self.window.key_events_enabled {
-            let handle = self.handle();
-            for (type_id, listener) in key_listeners {
-                let handle = handle.clone();
-                let listener = listener.clone();
-                self.window
-                    .key_listeners
-                    .entry(*type_id)
-                    .or_default()
-                    .push(Arc::new(move |event, phase, cx| {
+            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<'_, '_>| {
                         handle
                             .update(cx, |view, cx| listener(view, event, phase, cx))
-                            .log_err();
-                    }));
+                            .log_err()
+                            .flatten()
+                    },
+                );
+                self.window.key_listeners.push((event_type, listener));
             }
         }
 
         let result = f(self);
 
         if self.window.key_events_enabled {
-            for (type_id, _) in key_listeners {
-                self.window.key_listeners.get_mut(type_id).unwrap().pop();
-            }
+            let prev_len = self.window.key_listeners.len() - key_listeners.len();
+            self.window.key_listeners.truncate(prev_len);
         }
 
         result
@@ -1317,18 +1329,6 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
             })
         });
     }
-
-    pub fn on_keyboard_event<Event: 'static>(
-        &mut self,
-        handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
-    ) {
-        let handle = self.handle().upgrade(self).unwrap();
-        self.window_cx.on_keyboard_event(move |event, phase, cx| {
-            handle.update(cx, |view, cx| {
-                handler(view, event, phase, cx);
-            })
-        });
-    }
 }
 
 impl<'a, 'w, S: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, S> {