Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/element.rs      | 63 ++++++++++++++++++----
crates/gpui3/src/elements/div.rs | 94 +++++++++++++++------------------
crates/gpui3/src/elements/img.rs | 32 ++++++-----
crates/gpui3/src/elements/svg.rs | 32 ++++++-----
crates/gpui3/src/events.rs       |  5 -
crates/gpui3/src/focus.rs        | 23 ++++---
6 files changed, 144 insertions(+), 105 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -1,4 +1,7 @@
-use crate::{BorrowWindow, Bounds, ElementId, FocusHandle, LayoutId, Pixels, Point, ViewContext};
+use crate::{
+    BorrowWindow, Bounds, ElementId, FocusHandle, FocusListeners, LayoutId, Pixels, Point,
+    ViewContext,
+};
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 use std::mem;
@@ -55,36 +58,72 @@ impl ElementIdentity for Anonymous {
     }
 }
 
-pub trait ElementFocusability: 'static + Send + Sync {
+pub trait ElementFocusability<V: 'static + Send + Sync>: 'static + Send + Sync {
     fn focus_handle(&self) -> Option<&FocusHandle>;
-}
+    fn focus_listeners(&self) -> Option<&FocusListeners<V>>;
 
-pub struct Focusable(FocusHandle);
+    fn initialize<R>(
+        &self,
+        cx: &mut ViewContext<V>,
+        f: impl FnOnce(&mut ViewContext<V>) -> R,
+    ) -> R {
+        if let Some(focus_listeners) = self.focus_listeners() {
+            for listener in focus_listeners.iter().cloned() {
+                cx.on_focus_changed(move |view, event, cx| listener(view, event, cx));
+            }
+        }
 
-impl AsRef<FocusHandle> for Focusable {
-    fn as_ref(&self) -> &FocusHandle {
-        &self.0
+        if let Some(focus_handle) = self.focus_handle().cloned() {
+            cx.with_focus(focus_handle, |cx| f(cx))
+        } else {
+            f(cx)
+        }
     }
 }
 
-impl ElementFocusability for Focusable {
+pub struct Focusable<V: 'static + Send + Sync> {
+    pub focus_handle: FocusHandle,
+    pub focus_listeners: FocusListeners<V>,
+}
+
+impl<V> ElementFocusability<V> for Focusable<V>
+where
+    V: 'static + Send + Sync,
+{
     fn focus_handle(&self) -> Option<&FocusHandle> {
-        Some(&self.0)
+        Some(&self.focus_handle)
+    }
+
+    fn focus_listeners(&self) -> Option<&FocusListeners<V>> {
+        Some(&self.focus_listeners)
     }
 }
 
-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: Default::default(),
+        }
     }
 }
 
 pub struct NonFocusable;
 
-impl ElementFocusability for NonFocusable {
+impl<V> ElementFocusability<V> for NonFocusable
+where
+    V: 'static + Send + Sync,
+{
     fn focus_handle(&self) -> Option<&FocusHandle> {
         None
     }
+
+    fn focus_listeners(&self) -> Option<&FocusListeners<V>> {
+        None
+    }
 }
 
 pub trait ParentElement: Element {

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, EventListeners, Focus, FocusHandle,
+    FocusListeners, Focusable, GlobalElementId, 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;
@@ -61,6 +61,26 @@ 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>,
+    focus_style: StyleRefinement,
+    focus_in_style: StyleRefinement,
+    in_focus_style: StyleRefinement,
+    listeners: EventListeners<V>,
+}
+
 pub fn div<V>() -> Div<V, Anonymous, NonFocusable>
 where
     V: 'static + Send + Sync,
@@ -82,26 +102,6 @@ where
     }
 }
 
-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 +109,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> {
@@ -134,7 +134,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 {
@@ -364,7 +364,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(),
@@ -383,13 +383,17 @@ where
     }
 }
 
-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) {
@@ -408,7 +412,7 @@ where
 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,12 +430,8 @@ 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);
-
             if let Some(global_id) = global_id {
                 key_listeners.push((
                     TypeId::of::<KeyDownEvent>(),
@@ -451,17 +451,11 @@ 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;
 
@@ -554,7 +548,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 +559,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 +570,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,7 +581,7 @@ 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> {
@@ -598,7 +592,7 @@ where
 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 +606,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, EventListeners, Focus, FocusListeners,
+    Focusable, Hover, Identified, Interactive, 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,7 +154,7 @@ 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()
@@ -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, EventListeners, Focus, FocusListeners, Focusable, Hover,
+    Identified, Interactive, 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,7 +128,7 @@ 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()
@@ -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 🔗

@@ -268,9 +268,6 @@ pub type KeyListener<V> = Arc<
         + '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]>,
@@ -278,7 +275,6 @@ pub struct EventListeners<V: 'static> {
     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> {
@@ -290,7 +286,6 @@ impl<V> Default for EventListeners<V> {
             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