Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/element.rs              | 147 +++++++++++++++++++++++--
crates/gpui3/src/elements/div.rs         |  74 ++++--------
crates/gpui3/src/elements/img.rs         |  19 +-
crates/gpui3/src/elements/svg.rs         |  19 +-
crates/gpui3/src/interactive.rs          | 131 ++++-------------------
crates/ui2/src/components/icon_button.rs |   2 
crates/ui2/src/elements/button.rs        |   2 
crates/ui2/src/prelude.rs                |   4 
8 files changed, 207 insertions(+), 191 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -1,11 +1,15 @@
 use crate::{
-    BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, LayoutId,
-    MouseDownEvent, Pixels, Point, Style, StyleRefinement, ViewContext, WindowContext,
+    BorrowWindow, Bounds, DispatchPhase, ElementId, FocusHandle, FocusListeners, KeyDownEvent,
+    KeyListener, KeyMatch, LayoutId, MouseClickEvent, MouseClickListener, MouseDownEvent,
+    MouseDownListener, MouseMoveEvent, MouseMoveListener, MouseUpEvent, MouseUpListener, Pixels,
+    Point, ScrollWheelEvent, ScrollWheelListener, Style, StyleRefinement, ViewContext,
+    WindowContext,
 };
 use derive_more::{Deref, DerefMut};
+use parking_lot::Mutex;
 use refineable::Refineable;
 pub(crate) use smallvec::SmallVec;
-use std::{marker::PhantomData, mem};
+use std::{any::TypeId, mem, sync::Arc};
 
 pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
     type ViewState: 'static + Send + Sync;
@@ -40,19 +44,98 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement<Self::ViewState> {
 pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 
 pub trait ElementInteractivity<V: 'static + Send + Sync>: 'static + Send + Sync {
+    fn as_stateless(&self) -> &StatelessInteractivity<V>;
+    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
     fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
+    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
+
+    fn paint(
+        &mut self,
+        bounds: Bounds<Pixels>,
+        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
+        cx: &mut ViewContext<V>,
+    ) {
+        let stateless = self.as_stateless();
+        for listener in stateless.mouse_down_listeners.iter().cloned() {
+            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
+                listener(state, event, &bounds, phase, cx);
+            })
+        }
+
+        for listener in stateless.mouse_up_listeners.iter().cloned() {
+            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
+                listener(state, event, &bounds, phase, cx);
+            })
+        }
+
+        for listener in stateless.mouse_move_listeners.iter().cloned() {
+            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
+                listener(state, event, &bounds, phase, cx);
+            })
+        }
+
+        for listener in stateless.scroll_wheel_listeners.iter().cloned() {
+            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
+                listener(state, event, &bounds, phase, cx);
+            })
+        }
+
+        if let Some(stateful) = self.as_stateful() {
+            let click_listeners = stateful.mouse_click_listeners.clone();
+
+            let mouse_down = pending_click.lock().clone();
+            if let Some(mouse_down) = mouse_down {
+                cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
+                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
+                        let mouse_click = MouseClickEvent {
+                            down: mouse_down.clone(),
+                            up: event.clone(),
+                        };
+                        for listener in &click_listeners {
+                            listener(state, &mouse_click, cx);
+                        }
+                    }
+
+                    *pending_click.lock() = None;
+                });
+            } else {
+                cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
+                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
+                        *pending_click.lock() = Some(event.clone());
+                    }
+                });
+            };
+        }
+    }
 
     fn initialize<R>(
-        &self,
+        &mut self,
         cx: &mut ViewContext<V>,
-        f: impl FnOnce(Option<GlobalElementId>, &mut ViewContext<V>) -> R,
+        f: impl FnOnce(&mut ViewContext<V>) -> R,
     ) -> R {
-        if let Some(identified) = self.as_stateful() {
-            cx.with_element_id(identified.id.clone(), |global_id, cx| {
-                f(Some(global_id), cx)
+        if let Some(stateful) = self.as_stateful_mut() {
+            cx.with_element_id(stateful.id.clone(), |global_id, cx| {
+                stateful.key_listeners.push((
+                    TypeId::of::<KeyDownEvent>(),
+                    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, context)
+                            {
+                                return Some(action);
+                            }
+                        }
+
+                        None
+                    }),
+                ));
+                let result = stateful.stateless.initialize(cx, f);
+                stateful.key_listeners.pop();
+                result
             })
         } else {
-            f(None, cx)
+            cx.with_key_listeners(&self.as_stateless().key_listeners, f)
         }
     }
 }
@@ -62,7 +145,8 @@ pub struct StatefulInteractivity<V: 'static + Send + Sync> {
     pub id: ElementId,
     #[deref]
     #[deref_mut]
-    common: StatelessInteractivity<V>,
+    stateless: StatelessInteractivity<V>,
+    pub mouse_click_listeners: SmallVec<[MouseClickListener<V>; 2]>,
 }
 
 impl<V> ElementInteractivity<V> for StatefulInteractivity<V>
@@ -72,6 +156,18 @@ where
     fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
         Some(self)
     }
+
+    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
+        Some(self)
+    }
+
+    fn as_stateless(&self) -> &StatelessInteractivity<V> {
+        &self.stateless
+    }
+
+    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
+        &mut self.stateless
+    }
 }
 
 impl<V> From<ElementId> for StatefulInteractivity<V>
@@ -81,16 +177,29 @@ where
     fn from(id: ElementId) -> Self {
         Self {
             id,
-            common: StatelessInteractivity::default(),
+            stateless: StatelessInteractivity::default(),
+            mouse_click_listeners: SmallVec::new(),
         }
     }
 }
 
-pub struct StatelessInteractivity<V>(PhantomData<V>);
+pub struct StatelessInteractivity<V> {
+    pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
+    pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
+    pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
+    pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
+    pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
+}
 
 impl<V> Default for StatelessInteractivity<V> {
     fn default() -> Self {
-        Self(PhantomData)
+        Self {
+            mouse_down_listeners: SmallVec::new(),
+            mouse_up_listeners: SmallVec::new(),
+            mouse_move_listeners: SmallVec::new(),
+            scroll_wheel_listeners: SmallVec::new(),
+            key_listeners: SmallVec::new(),
+        }
     }
 }
 
@@ -101,6 +210,18 @@ where
     fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
         None
     }
+
+    fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
+        None
+    }
+
+    fn as_stateless(&self) -> &StatelessInteractivity<V> {
+        self
+    }
+
+    fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
+        self
+    }
 }
 
 pub trait ElementFocusability<V: 'static + Send + Sync>: 'static + Send + Sync {

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

@@ -1,16 +1,16 @@
 use crate::{
-    Active, AnyElement, AppContext, BorrowWindow, Bounds, Click, DispatchPhase, Element,
+    Active, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element,
     ElementFocusability, ElementId, ElementInteractivity, Focus, FocusHandle, FocusListeners,
-    Focusable, GlobalElementId, Hover, Interactive, Interactivity, IntoAnyElement, KeyDownEvent,
-    KeyMatch, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, NonFocusable, Overflow,
-    ParentElement, Pixels, Point, SharedString, StatefulInteractivity, StatelessInteractivity,
+    Focusable, GlobalElementId, Hover, IntoAnyElement, LayoutId, MouseDownEvent, MouseMoveEvent,
+    MouseUpEvent, NonFocusable, Overflow, ParentElement, Pixels, Point, SharedString,
+    StatefulInteractivity, StatefullyInteractive, StatelessInteractivity, StatelesslyInteractive,
     Style, StyleRefinement, Styled, ViewContext,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
 use refineable::Refineable;
 use smallvec::SmallVec;
-use std::{any::TypeId, mem, sync::Arc};
+use std::sync::Arc;
 
 #[derive(Default)]
 pub struct DivState {
@@ -66,9 +66,8 @@ pub struct Div<
     I: ElementInteractivity<V> = StatelessInteractivity<V>,
     F: ElementFocusability<V> = NonFocusable,
 > {
-    identity: I,
+    interactivity: I,
     focusability: F,
-    interactivity: Interactivity<V>,
     children: SmallVec<[AnyElement<V>; 2]>,
     group: Option<SharedString>,
     base_style: StyleRefinement,
@@ -83,9 +82,8 @@ where
     V: 'static + Send + Sync,
 {
     Div {
-        identity: StatelessInteractivity::default(),
+        interactivity: StatelessInteractivity::default(),
         focusability: NonFocusable,
-        interactivity: Interactivity::default(),
         children: SmallVec::new(),
         group: None,
         base_style: StyleRefinement::default(),
@@ -108,9 +106,8 @@ where
 {
     pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteractivity<V>, F> {
         Div {
-            identity: id.into().into(),
+            interactivity: id.into().into(),
             focusability: self.focusability,
-            interactivity: self.interactivity,
             children: self.children,
             group: self.group,
             base_style: self.base_style,
@@ -277,7 +274,7 @@ where
 {
     pub fn focusable(self, handle: &FocusHandle) -> Div<V, I, Focusable<V>> {
         Div {
-            identity: self.identity,
+            interactivity: self.interactivity,
             focusability: handle.clone().into(),
             children: self.children,
             group: self.group,
@@ -286,7 +283,6 @@ where
             group_hover: self.group_hover,
             active_style: self.active_style,
             group_active: self.group_active,
-            interactivity: self.interactivity,
         }
     }
 }
@@ -327,7 +323,7 @@ where
     type ElementState = DivState;
 
     fn id(&self) -> Option<ElementId> {
-        self.identity
+        self.interactivity
             .as_stateful()
             .map(|identified| identified.id.clone())
     }
@@ -338,39 +334,14 @@ where
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
     ) -> Self::ElementState {
-        self.with_element_id(cx, |this, global_id, cx| {
-            let element_state = element_state.unwrap_or_default();
-
-            let mut key_listeners = mem::take(&mut this.interactivity.key);
-            if let Some(global_id) = global_id {
-                key_listeners.push((
-                    TypeId::of::<KeyDownEvent>(),
-                    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, context)
-                            {
-                                return Some(action);
-                            }
-                        }
-
-                        None
-                    }),
-                ));
-            }
-
-            cx.with_key_listeners(&key_listeners, |cx| {
-                this.focusability.initialize(cx, |cx| {
-                    for child in &mut this.children {
-                        child.initialize(view_state, cx);
-                    }
-                });
+        self.interactivity.initialize(cx, |cx| {
+            self.focusability.initialize(cx, |cx| {
+                for child in &mut self.children {
+                    child.initialize(view_state, cx);
+                }
             });
-            this.interactivity.key = key_listeners;
-
-            element_state
-        })
+        });
+        element_state.unwrap_or_default()
     }
 
     fn layout(
@@ -490,14 +461,14 @@ where
     }
 }
 
-impl<V, I, F> Interactive for Div<V, I, F>
+impl<V, I, F> StatelesslyInteractive for Div<V, I, F>
 where
     I: ElementInteractivity<V>,
     F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
-    fn interactivity(&mut self) -> &mut Interactivity<V> {
-        &mut self.interactivity
+    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
+        self.interactivity.as_stateless_mut()
     }
 }
 
@@ -516,11 +487,14 @@ where
     }
 }
 
-impl<V, F> Click for Div<V, StatefulInteractivity<V>, F>
+impl<V, F> StatefullyInteractive for Div<V, StatefulInteractivity<V>, F>
 where
     F: ElementFocusability<V>,
     V: 'static + Send + Sync,
 {
+    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState> {
+        &mut self.interactivity
+    }
 }
 
 impl<V, F> Active for Div<V, StatefulInteractivity<V>, F>

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

@@ -1,8 +1,8 @@
 use crate::{
-    div, Active, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
-    ElementFocusability, ElementId, ElementInteractivity, Focus, FocusListeners, Focusable, Hover,
-    Interactive, Interactivity, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
-    StatefulInteractivity, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
+    div, Active, AnyElement, BorrowWindow, Bounds, Div, DivState, Element, ElementFocusability,
+    ElementId, ElementInteractivity, Focus, FocusListeners, Focusable, Hover, IntoAnyElement,
+    LayoutId, NonFocusable, Pixels, SharedString, StatefulInteractivity, StatefullyInteractive,
+    StatelessInteractivity, StatelesslyInteractive, StyleRefinement, Styled, ViewContext,
 };
 use futures::FutureExt;
 use util::ResultExt;
@@ -150,14 +150,14 @@ where
     }
 }
 
-impl<V, I, F> Interactive for Img<V, I, F>
+impl<V, I, F> StatelesslyInteractive for Img<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementInteractivity<V>,
     F: ElementFocusability<V>,
 {
-    fn interactivity(&mut self) -> &mut Interactivity<V> {
-        self.base.interactivity()
+    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
+        self.base.stateless_interactivity()
     }
 }
 
@@ -172,11 +172,14 @@ where
     }
 }
 
-impl<V, F> Click for Img<V, StatefulInteractivity<V>, F>
+impl<V, F> StatefullyInteractive for Img<V, StatefulInteractivity<V>, F>
 where
     V: 'static + Send + Sync,
     F: ElementFocusability<V>,
 {
+    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState> {
+        self.base.stateful_interactivity()
+    }
 }
 
 impl<V, F> Active for Img<V, StatefulInteractivity<V>, F>

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

@@ -1,8 +1,8 @@
 use crate::{
-    div, Active, AnyElement, Bounds, Click, Div, DivState, Element, ElementFocusability, ElementId,
-    ElementInteractivity, Focus, FocusListeners, Focusable, Hover, Interactive, Interactivity,
-    IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString, StatefulInteractivity,
-    StatelessInteractivity, StyleRefinement, Styled, ViewContext,
+    div, Active, AnyElement, Bounds, Div, DivState, Element, ElementFocusability, ElementId,
+    ElementInteractivity, Focus, FocusListeners, Focusable, Hover, IntoAnyElement, LayoutId,
+    NonFocusable, Pixels, SharedString, StatefulInteractivity, StatefullyInteractive,
+    StatelessInteractivity, StatelesslyInteractive, StyleRefinement, Styled, ViewContext,
 };
 use util::ResultExt;
 
@@ -124,14 +124,14 @@ where
     }
 }
 
-impl<V, I, F> Interactive for Svg<V, I, F>
+impl<V, I, F> StatelesslyInteractive for Svg<V, I, F>
 where
     V: 'static + Send + Sync,
     I: ElementInteractivity<V>,
     F: ElementFocusability<V>,
 {
-    fn interactivity(&mut self) -> &mut Interactivity<V> {
-        self.base.interactivity()
+    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
+        self.base.stateless_interactivity()
     }
 }
 
@@ -146,11 +146,14 @@ where
     }
 }
 
-impl<V, F> Click for Svg<V, StatefulInteractivity<V>, F>
+impl<V, F> StatefullyInteractive for Svg<V, StatefulInteractivity<V>, F>
 where
     V: 'static + Send + Sync,
     F: ElementFocusability<V>,
 {
+    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState> {
+        self.base.stateful_interactivity()
+    }
 }
 
 impl<V, F> Active for Svg<V, StatefulInteractivity<V>, F>

crates/gpui3/src/interactive.rs 🔗

@@ -1,19 +1,15 @@
-use parking_lot::Mutex;
-use smallvec::SmallVec;
-
 use crate::{
     point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, Keystroke,
-    Modifiers, Pixels, Point, ViewContext,
+    Modifiers, Pixels, Point, StatefulInteractivity, StatelessInteractivity, ViewContext,
 };
 use std::{
     any::{Any, TypeId},
-    mem,
     ops::Deref,
     sync::Arc,
 };
 
-pub trait Interactive: Element {
-    fn interactivity(&mut self) -> &mut Interactivity<Self::ViewState>;
+pub trait StatelesslyInteractive: Element {
+    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<Self::ViewState>;
 
     fn on_mouse_down(
         mut self,
@@ -26,8 +22,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_down
+        self.stateless_interactivity()
+            .mouse_down_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
@@ -50,8 +46,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_up
+        self.stateless_interactivity()
+            .mouse_up_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble
                     && event.button == button
@@ -74,8 +70,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_down
+        self.stateless_interactivity()
+            .mouse_down_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
@@ -98,8 +94,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_up
+        self.stateless_interactivity()
+            .mouse_up_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Capture
                     && event.button == button
@@ -121,8 +117,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_move
+        self.stateless_interactivity()
+            .mouse_move_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
@@ -141,8 +137,8 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .scroll_wheel
+        self.stateless_interactivity()
+            .scroll_wheel_listeners
             .push(Arc::new(move |view, event, bounds, phase, cx| {
                 if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
                     handler(view, event, cx);
@@ -165,7 +161,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity().key.push((
+        self.stateless_interactivity().key_listeners.push((
             TypeId::of::<KeyDownEvent>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -186,7 +182,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity().key.push((
+        self.stateless_interactivity().key_listeners.push((
             TypeId::of::<KeyUpEvent>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -207,7 +203,7 @@ pub trait Interactive: Element {
     where
         Self: Sized,
     {
-        self.interactivity().key.push((
+        self.stateless_interactivity().key_listeners.push((
             TypeId::of::<A>(),
             Arc::new(move |view, event, _, phase, cx| {
                 let event = event.downcast_ref().unwrap();
@@ -219,7 +215,9 @@ pub trait Interactive: Element {
     }
 }
 
-pub trait Click: Interactive {
+pub trait StatefullyInteractive: StatelesslyInteractive {
+    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState>;
+
     fn on_click(
         mut self,
         handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
@@ -230,8 +228,8 @@ pub trait Click: Interactive {
     where
         Self: Sized,
     {
-        self.interactivity()
-            .mouse_click
+        self.stateful_interactivity()
+            .mouse_click_listeners
             .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
         self
     }
@@ -495,86 +493,3 @@ pub type KeyListener<V> = Arc<
         + Sync
         + 'static,
 >;
-
-pub struct Interactivity<V> {
-    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 Interactivity<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(),
-        }
-    }
-}
-
-impl<V> Interactivity<V>
-where
-    V: 'static + Send + Sync,
-{
-    pub fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        pending_click: Arc<Mutex<Option<MouseDownEvent>>>,
-        cx: &mut ViewContext<V>,
-    ) {
-        let click_listeners = mem::take(&mut self.mouse_click);
-
-        let mouse_down = pending_click.lock().clone();
-        if let Some(mouse_down) = mouse_down {
-            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    let mouse_click = MouseClickEvent {
-                        down: mouse_down.clone(),
-                        up: event.clone(),
-                    };
-                    for listener in &click_listeners {
-                        listener(state, &mouse_click, cx);
-                    }
-                }
-
-                *pending_click.lock() = None;
-            });
-        } else {
-            cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    *pending_click.lock() = Some(event.clone());
-                }
-            });
-        }
-
-        for listener in mem::take(&mut self.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.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.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.scroll_wheel) {
-            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
-                listener(state, event, &bounds, phase, cx);
-            })
-        }
-    }
-}

crates/ui2/src/components/icon_button.rs 🔗

@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 use std::sync::Arc;
 
-use gpui3::{Interactive, MouseButton};
+use gpui3::{MouseButton, StatelesslyInteractive};
 
 use crate::{h_stack, prelude::*};
 use crate::{theme, ClickHandler, Icon, IconColor, IconElement};

crates/ui2/src/elements/button.rs 🔗

@@ -1,7 +1,7 @@
 use std::marker::PhantomData;
 use std::sync::Arc;
 
-use gpui3::{DefiniteLength, Hsla, Interactive, MouseButton, WindowContext};
+use gpui3::{DefiniteLength, Hsla, MouseButton, StatelesslyInteractive, WindowContext};
 
 use crate::settings::user_settings;
 use crate::{h_stack, Icon, IconColor, IconElement, Label, LabelColor};

crates/ui2/src/prelude.rs 🔗

@@ -1,6 +1,6 @@
 pub use gpui3::{
-    div, Active, Click, Element, Hover, IntoAnyElement, ParentElement, ScrollState, SharedString,
-    Styled, ViewContext, WindowContext,
+    div, Click, Element, Hover, IntoAnyElement, ParentElement, ScrollState, SharedString,
+    StatefullyInteractivee, Styled, ViewContext, WindowContext,
 };
 
 use crate::settings::user_settings;