Click events, children

Nathan Sobo created

Change summary

crates/gpui/playground/src/div.rs           |  41 +-----
crates/gpui/playground/src/hoverable.rs     |  24 +++
crates/gpui/playground/src/interactive.rs   | 137 ++++++++++++++++++++--
crates/gpui/playground/src/paint_context.rs |   2 
crates/gpui/playground/src/playground.rs    |  10 +
crates/gpui/playground/src/pressable.rs     |  24 +++
crates/gpui/playground/src/style.rs         |  16 ++
7 files changed, 206 insertions(+), 48 deletions(-)

Detailed changes

crates/gpui/playground/src/div.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    element::{AnyElement, Element, Layout, ParentElement},
+    element::{AnyElement, Element, IntoElement, Layout, ParentElement},
     interactive::{InteractionHandlers, Interactive},
     layout_context::LayoutContext,
     paint_context::PaintContext,
@@ -47,6 +47,8 @@ impl<V: 'static> Element<V> for Div<V> {
     {
         self.computed_style()
             .paint_background(layout.bounds(cx), cx);
+        self.interaction_handlers()
+            .paint(layout.order(cx), layout.bounds(cx), cx);
         for child in &mut self.children {
             child.paint(view, cx);
         }
@@ -79,35 +81,10 @@ impl<V: 'static> ParentElement<V> for Div<V> {
     }
 }
 
-#[test]
-fn test() {
-    // let elt = div().w_auto();
-}
-
-// trait Element<V: 'static> {
-//     type Style;
-
-//     fn layout()
-// }
-
-// trait Stylable<V: 'static>: Element<V> {
-//     type Style;
-
-//     fn with_style(self, style: Self::Style) -> Self;
-// }
+impl<V: 'static> IntoElement<V> for Div<V> {
+    type Element = Self;
 
-// pub struct HoverStyle<S> {
-//     default: S,
-//     hovered: S,
-// }
-
-// struct Hover<V: 'static, C: Stylable<V>> {
-//     child: C,
-//     style: HoverStyle<C::Style>,
-// }
-
-// impl<V: 'static, C: Stylable<V>> Hover<V, C> {
-//     fn new(child: C, style: HoverStyle<C::Style>) -> Self {
-//         Self { child, style }
-//     }
-// }
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}

crates/gpui/playground/src/hoverable.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
-    element::{Element, Layout},
+    element::{AnyElement, Element, IntoElement, Layout, ParentElement},
+    interactive::{InteractionHandlers, Interactive},
     layout_context::LayoutContext,
     paint_context::PaintContext,
     style::{Style, StyleHelpers, Styleable},
@@ -7,6 +8,7 @@ use crate::{
 use anyhow::Result;
 use gpui::platform::MouseMovedEvent;
 use refineable::{CascadeSlot, Refineable, RefinementCascade};
+use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc};
 
 pub struct Hoverable<E: Styleable> {
@@ -76,3 +78,23 @@ impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
 }
 
 impl<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}
+
+impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Hoverable<E> {
+    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
+        self.child.interaction_handlers()
+    }
+}
+
+impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Hoverable<E> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        self.child.children_mut()
+    }
+}
+
+impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Hoverable<E> {
+    type Element = Self;
+
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}

crates/gpui/playground/src/interactive.rs 🔗

@@ -1,34 +1,147 @@
-use gpui::{platform::MouseMovedEvent, EventContext};
+use gpui::{
+    geometry::rect::RectF,
+    platform::{MouseButton, MouseButtonEvent},
+    EventContext,
+};
 use smallvec::SmallVec;
-use std::rc::Rc;
+use std::{cell::Cell, rc::Rc};
+
+use crate::element::PaintContext;
 
 pub trait Interactive<V: 'static> {
     fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
 
-    fn on_mouse_move<H>(mut self, handler: H) -> Self
+    fn on_mouse_down(
+        mut self,
+        button: MouseButton,
+        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+    ) -> Self
     where
-        H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>),
         Self: Sized,
     {
         self.interaction_handlers()
-            .mouse_moved
-            .push(Rc::new(move |view, event, hit_test, cx| {
-                handler(view, event, hit_test, cx);
-                cx.bubble
-            }));
+            .mouse_down
+            .push(Rc::new(handler));
         self
     }
+
+    fn on_mouse_up(
+        mut self,
+        button: MouseButton,
+        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.interaction_handlers().mouse_up.push(Rc::new(handler));
+        self
+    }
+
+    fn on_mouse_down_out(
+        mut self,
+        button: MouseButton,
+        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.interaction_handlers()
+            .mouse_down_out
+            .push(Rc::new(handler));
+        self
+    }
+
+    fn on_mouse_up_out(
+        mut self,
+        button: MouseButton,
+        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        self.interaction_handlers()
+            .mouse_up_out
+            .push(Rc::new(handler));
+        self
+    }
+
+    fn on_click(
+        self,
+        button: MouseButton,
+        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
+    ) -> Self
+    where
+        Self: Sized,
+    {
+        let pressed = Rc::new(Cell::new(false));
+        self.on_mouse_down(button, {
+            let pressed = pressed.clone();
+            move |_, _, _| {
+                pressed.set(true);
+            }
+        })
+        .on_mouse_up_out(button, {
+            let pressed = pressed.clone();
+            move |_, _, _| {
+                pressed.set(false);
+            }
+        })
+        .on_mouse_up(button, move |view, event, cx| {
+            if pressed.get() {
+                pressed.set(false);
+                handler(view, event, cx);
+            }
+        })
+    }
 }
 
 pub struct InteractionHandlers<V: 'static> {
-    mouse_moved:
-        SmallVec<[Rc<dyn Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>) -> bool>; 2]>,
+    mouse_down: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
+    mouse_down_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
+    mouse_up: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
+    mouse_up_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
+}
+
+impl<V: 'static> InteractionHandlers<V> {
+    pub fn paint(&self, order: u32, bounds: RectF, cx: &mut PaintContext<V>) {
+        for handler in self.mouse_down.iter().cloned() {
+            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+                if event.is_down && bounds.contains_point(event.position) {
+                    handler(view, event, cx);
+                }
+            })
+        }
+        for handler in self.mouse_up.iter().cloned() {
+            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+                if !event.is_down && bounds.contains_point(event.position) {
+                    handler(view, event, cx);
+                }
+            })
+        }
+        for handler in self.mouse_down_out.iter().cloned() {
+            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+                if event.is_down && !bounds.contains_point(event.position) {
+                    handler(view, event, cx);
+                }
+            })
+        }
+        for handler in self.mouse_up_out.iter().cloned() {
+            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
+                if !event.is_down && !bounds.contains_point(event.position) {
+                    handler(view, event, cx);
+                }
+            })
+        }
+    }
 }
 
 impl<V> Default for InteractionHandlers<V> {
     fn default() -> Self {
         Self {
-            mouse_moved: Default::default(),
+            mouse_down: Default::default(),
+            mouse_up: Default::default(),
+            mouse_down_out: Default::default(),
+            mouse_up_out: Default::default(),
         }
     }
 }

crates/gpui/playground/src/paint_context.rs 🔗

@@ -42,7 +42,7 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
     pub fn on_event<E: 'static>(
         &mut self,
         order: u32,
-        handler: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
+        handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
     ) {
         let view = self.weak_handle();
 

crates/gpui/playground/src/playground.rs 🔗

@@ -1,6 +1,7 @@
 #![allow(dead_code, unused_variables)]
 use crate::{
     color::black,
+    element::ParentElement,
     style::{StyleHelpers, Styleable},
 };
 use element::Element;
@@ -59,7 +60,14 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
         .fill(theme.error(0.5))
         .pressed()
         .fill(theme.warning(0.5))
-    // .child(button().label("Hello").click(|_, _, _| println!("click!")))
+        .child(
+            div()
+                .h_6()
+                .w_6()
+                .absolute()
+                .bottom_0()
+                .fill(theme.success(0.)),
+        )
 }
 
 //     todo!()

crates/gpui/playground/src/pressable.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
-    element::{Element, Layout},
+    element::{AnyElement, Element, IntoElement, Layout, ParentElement},
+    interactive::{InteractionHandlers, Interactive},
     layout_context::LayoutContext,
     paint_context::PaintContext,
     style::{Style, StyleHelpers, Styleable},
@@ -7,6 +8,7 @@ use crate::{
 use anyhow::Result;
 use gpui::platform::MouseButtonEvent;
 use refineable::{CascadeSlot, Refineable, RefinementCascade};
+use smallvec::SmallVec;
 use std::{cell::Cell, rc::Rc};
 
 pub struct Pressable<E: Styleable> {
@@ -79,3 +81,23 @@ 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()
+    }
+}
+
+impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Pressable<E> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        self.child.children_mut()
+    }
+}
+
+impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Pressable<E> {
+    type Element = Self;
+
+    fn into_element(self) -> Self::Element {
+        self
+    }
+}

crates/gpui/playground/src/style.rs 🔗

@@ -274,6 +274,22 @@ pub trait Styleable {
 pub trait StyleHelpers: Styleable<Style = Style> {
     styleable_helpers!();
 
+    fn relative(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().position = Some(Position::Relative);
+        self
+    }
+
+    fn absolute(mut self) -> Self
+    where
+        Self: Sized,
+    {
+        self.declared_style().position = Some(Position::Absolute);
+        self
+    }
+
     fn fill<F>(mut self, fill: F) -> Self
     where
         F: Into<Fill>,