Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/element.rs            | 10 ++-
crates/gpui3/src/elements.rs           |  1 
crates/gpui3/src/elements/div.rs       | 12 ++--
crates/gpui3/src/elements/hoverable.rs | 71 ++++++++++++++++-----------
crates/gpui3/src/elements/img.rs       |  6 -
crates/gpui3/src/elements/pressable.rs |  2 
crates/gpui3/src/elements/svg.rs       |  4 -
crates/gpui3/src/style_helpers.rs      |  2 
crates/gpui3/src/styled.rs             |  4 
crates/gpui3/src/window.rs             | 13 +++-
crates/storybook2/src/collab_panel.rs  |  5 +
11 files changed, 76 insertions(+), 54 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -22,10 +22,12 @@ pub trait Element: 'static {
     ) -> Result<()>;
 }
 
-pub trait ParentElement<S> {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]>;
+pub trait ParentElement {
+    type State;
+
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]>;
 
-    fn child(mut self, child: impl IntoAnyElement<S>) -> Self
+    fn child(mut self, child: impl IntoAnyElement<Self::State>) -> Self
     where
         Self: Sized,
     {
@@ -33,7 +35,7 @@ pub trait ParentElement<S> {
         self
     }
 
-    fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<S>>) -> Self
+    fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<Self::State>>) -> Self
     where
         Self: Sized,
     {

crates/gpui3/src/elements.rs 🔗

@@ -6,6 +6,7 @@ mod svg;
 mod text;
 
 pub use div::*;
+pub use hoverable::*;
 pub use img::*;
 pub use stateless::*;
 pub use svg::*;

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

@@ -1,7 +1,7 @@
 use crate::{
     AnyElement, Bounds, Element, Interactive, LayoutId, MouseEventListeners, Overflow,
-    ParentElement, Pixels, Point, Refineable, RefinementCascade, Result, Style, StyleHelpers,
-    Styled, ViewContext,
+    ParentElement, Pixels, Point, Refineable, RefinementCascade, Result, Style, Styled,
+    ViewContext,
 };
 use parking_lot::Mutex;
 use smallvec::SmallVec;
@@ -249,16 +249,16 @@ impl<V> Styled for Div<V> {
     }
 }
 
-impl<V> StyleHelpers for Div<V> {}
-
 impl<V: Send + Sync + 'static> Interactive<V> for Div<V> {
     fn listeners(&mut self) -> &mut MouseEventListeners<V> {
         &mut self.listeners
     }
 }
 
-impl<V: 'static> ParentElement<V> for Div<V> {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+impl<S: 'static> ParentElement for Div<S> {
+    type State = S;
+
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]> {
         &mut self.children
     }
 }

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

@@ -1,26 +1,33 @@
 use crate::{
-    Bounds, Element, Interactive, MouseEventListeners, Pixels, Style, Styled, ViewContext,
+    AnyElement, Bounds, DispatchPhase, Element, Interactive, MouseEventListeners, MouseMoveEvent,
+    ParentElement, Pixels, Styled, ViewContext,
 };
 use anyhow::Result;
-use refineable::{CascadeSlot, RefinementCascade};
-use std::{cell::Cell, rc::Rc};
-
-pub fn hoverable<E: Styled>(mut child: E) -> Hoverable<E> {
-    Hoverable {
-        hovered: Rc::new(Cell::new(false)),
-        cascade_slot: child.style_cascade().reserve(),
-        hovered_style: Default::default(),
-        child,
-    }
-}
+use refineable::{CascadeSlot, Refineable, RefinementCascade};
+use smallvec::SmallVec;
+use std::sync::{
+    atomic::{AtomicBool, Ordering::SeqCst},
+    Arc,
+};
 
 pub struct Hoverable<E: Styled> {
-    hovered: Rc<Cell<bool>>,
+    hovered: Arc<AtomicBool>,
     cascade_slot: CascadeSlot,
-    hovered_style: RefinementCascade<E::Style>,
+    hovered_style: <E::Style as Refineable>::Refinement,
     child: E,
 }
 
+impl<E: Styled> Hoverable<E> {
+    pub fn new(mut child: E) -> Self {
+        Self {
+            hovered: Arc::new(AtomicBool::new(false)),
+            cascade_slot: child.style_cascade().reserve(),
+            hovered_style: Default::default(),
+            child,
+        }
+    }
+}
+
 impl<E> Styled for Hoverable<E>
 where
     E: Styled,
@@ -61,22 +68,30 @@ impl<E: Element + Styled> Element for Hoverable<E> {
         frame_state: &mut Self::FrameState,
         cx: &mut ViewContext<Self::State>,
     ) -> Result<()> {
-        todo!()
-        // self.hovered.set(bounds.contains_point(cx.mouse_position()));
+        let hovered = bounds.contains_point(cx.mouse_position());
+        let slot = self.cascade_slot;
+        let style = hovered.then_some(self.hovered_style.clone());
+        self.style_cascade().set(slot, style);
+        self.hovered.store(hovered, SeqCst);
 
-        // let slot = self.cascade_slot;
-        // let style = self.hovered.get().then_some(self.hovered_style.clone());
-        // self.style_cascade().set(slot, style);
+        let hovered = self.hovered.clone();
+        cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
+            if phase == DispatchPhase::Capture {
+                if bounds.contains_point(event.position) != hovered.load(SeqCst) {
+                    cx.notify();
+                }
+            }
+        });
 
-        // let hovered = self.hovered.clone();
-        // cx.on_event(layout.order, move |_view, _: &MouseMovedEvent, cx| {
-        //     cx.bubble_event();
-        //     if bounds.contains_point(cx.mouse_position()) != hovered.get() {
-        //         cx.repaint();
-        //     }
-        // });
+        self.child.paint(bounds, state, frame_state, cx)?;
+        Ok(())
+    }
+}
+
+impl<E: ParentElement + Styled> ParentElement for Hoverable<E> {
+    type State = E::State;
 
-        // self.child
-        //     .paint(view, parent_origin, layout, paint_state, cx);
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+        self.child.children_mut()
     }
 }

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

@@ -1,6 +1,6 @@
 use crate::{
-    BorrowWindow, Bounds, Element, LayoutId, Pixels, Result, SharedString, Style, StyleHelpers,
-    Styled, ViewContext,
+    BorrowWindow, Bounds, Element, LayoutId, Pixels, Result, SharedString, Style, Styled,
+    ViewContext,
 };
 use futures::FutureExt;
 use refineable::RefinementCascade;
@@ -98,5 +98,3 @@ impl<S> Styled for Img<S> {
         self.style.base()
     }
 }
-
-impl<S> StyleHelpers for Img<S> {}

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

@@ -85,8 +85,6 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
     }
 }
 
-impl<E: Styleable<Style = Style>> StyleHelpers for Pressable<E> {}
-
 impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Pressable<E> {
     fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
         self.child.interaction_handlers()

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

@@ -1,4 +1,4 @@
-use crate::{Bounds, Element, LayoutId, Pixels, Result, SharedString, Style, StyleHelpers, Styled};
+use crate::{Bounds, Element, LayoutId, Pixels, Result, SharedString, Style, Styled};
 use refineable::RefinementCascade;
 use std::marker::PhantomData;
 
@@ -68,5 +68,3 @@ impl<S> Styled for Svg<S> {
         self.style.base()
     }
 }
-
-impl<S> StyleHelpers for Svg<S> {}

crates/gpui3/src/styled.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{Refineable, RefinementCascade};
+use crate::{Hoverable, Refineable, RefinementCascade};
 
 pub trait Styled {
     type Style: Refineable + Default;
@@ -14,7 +14,7 @@ pub trait Styled {
     where
         Self: Sized,
     {
-        hoverable(self)
+        Hoverable::new(self)
     }
 
     // fn active(self) -> Pressable<Self>

crates/gpui3/src/window.rs 🔗

@@ -2,10 +2,11 @@ use crate::{
     image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext,
     AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId,
     Edges, Effect, Element, EntityId, Event, FontId, GlyphId, Handle, Hsla, ImageData, IsZero,
-    LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas,
-    PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams,
-    ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, TaffyLayoutEngine, Task,
-    Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
+    LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels,
+    PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
+    RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style,
+    TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions,
+    SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use collections::HashMap;
@@ -619,6 +620,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
     fn dispatch_event(&mut self, event: Event) -> bool {
         if let Some(any_mouse_event) = event.mouse_event() {
+            if let Some(MouseMoveEvent { position, .. }) = any_mouse_event.downcast_ref() {
+                self.window.mouse_position = *position;
+            }
+
             if let Some(mut handlers) = self
                 .window
                 .mouse_event_handlers

crates/storybook2/src/collab_panel.rs 🔗

@@ -1,7 +1,8 @@
 use crate::theme::{theme, Theme};
 use gpui3::{
     div, img, svg, view, AppContext, Context, Element, Interactive, IntoAnyElement, MouseButton,
-    ParentElement, ScrollState, SharedString, StyleHelpers, View, ViewContext, WindowContext,
+    ParentElement, ScrollState, SharedString, StyleHelpers, Styled, View, ViewContext,
+    WindowContext,
 };
 
 pub struct CollabPanel {
@@ -129,6 +130,8 @@ impl CollabPanel {
             .flex()
             .justify_between()
             .items_center()
+            .hover()
+            .fill(theme.lowest.base.active.background)
             .child(div().flex().gap_1().text_sm().child(label))
             .child(
                 div().flex().h_full().gap_1().items_center().child(