WIP

Nathan Sobo created

Change summary

crates/gpui/playground/src/adapter.rs        | 124 ++--
crates/gpui/playground/src/div.rs            | 167 -----
crates/gpui/playground/src/element.rs        | 591 +++------------------
crates/gpui/playground/src/frame.rs          |  84 ---
crates/gpui/playground/src/hoverable.rs      | 143 +---
crates/gpui/playground/src/layout_context.rs |   2 
crates/gpui/playground/src/playground.rs     |   1 
crates/gpui/playground/src/style.rs          |  27 
crates/gpui/playground/src/text.rs           |  36 
crates/gpui/playground/src/view.rs           |   2 
10 files changed, 248 insertions(+), 929 deletions(-)

Detailed changes

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

@@ -1,70 +1,70 @@
-use crate::element::{LayoutContext, PaintContext};
-use gpui::{geometry::rect::RectF, LayoutEngine};
-use util::ResultExt;
+// use crate::element::{LayoutContext, PaintContext};
+// use gpui::{geometry::rect::RectF, LayoutEngine};
+// use util::ResultExt;
 
-use crate::element::AnyElement;
+// use crate::element::AnyElement;
 
-pub struct Adapter<V>(pub(crate) AnyElement<V>);
+// pub struct Adapter<V>(pub(crate) AnyElement<V>);
 
-impl<V: 'static> gpui::Element<V> for Adapter<V> {
-    type LayoutState = Option<LayoutEngine>;
-    type PaintState = ();
+// impl<V: 'static> gpui::Element<V> for Adapter<V> {
+//     type LayoutState = Option<LayoutEngine>;
+//     type PaintState = ();
 
-    fn layout(
-        &mut self,
-        constraint: gpui::SizeConstraint,
-        view: &mut V,
-        cx: &mut LayoutContext<V>,
-    ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
-        cx.push_layout_engine(LayoutEngine::new());
-        let node = self.0.layout(view, cx).log_err();
+//     fn layout(
+//         &mut self,
+//         constraint: gpui::SizeConstraint,
+//         view: &mut V,
+//         cx: &mut LayoutContext<V>,
+//     ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+//         cx.push_layout_engine(LayoutEngine::new());
+//         let node = self.0.layout(view, cx).log_err();
 
-        if let Some(node) = node {
-            let layout_engine = cx.layout_engine().unwrap();
-            layout_engine.compute_layout(node, constraint.max).log_err();
-        }
-        let layout_engine = cx.pop_layout_engine();
-        debug_assert!(layout_engine.is_some());
-        (constraint.max, layout_engine)
-    }
+//         if let Some(node) = node {
+//             let layout_engine = cx.layout_engine().unwrap();
+//             layout_engine.compute_layout(node, constraint.max).log_err();
+//         }
+//         let layout_engine = cx.pop_layout_engine();
+//         debug_assert!(layout_engine.is_some());
+//         (constraint.max, layout_engine)
+//     }
 
-    fn paint(
-        &mut self,
-        scene: &mut gpui::SceneBuilder,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout_engine: &mut Option<LayoutEngine>,
-        view: &mut V,
-        legacy_cx: &mut gpui::PaintContext<V>,
-    ) -> Self::PaintState {
-        legacy_cx.push_layout_engine(layout_engine.take().unwrap());
-        let mut cx = PaintContext::new(legacy_cx, scene);
-        self.0.paint(view, &mut cx).log_err();
-        *layout_engine = legacy_cx.pop_layout_engine();
-        debug_assert!(layout_engine.is_some());
-    }
+//     fn paint(
+//         &mut self,
+//         scene: &mut gpui::SceneBuilder,
+//         bounds: RectF,
+//         visible_bounds: RectF,
+//         layout_engine: &mut Option<LayoutEngine>,
+//         view: &mut V,
+//         legacy_cx: &mut gpui::PaintContext<V>,
+//     ) -> Self::PaintState {
+//         legacy_cx.push_layout_engine(layout_engine.take().unwrap());
+//         let mut cx = PaintContext::new(legacy_cx, scene);
+//         self.0.paint(view, &mut cx).log_err();
+//         *layout_engine = legacy_cx.pop_layout_engine();
+//         debug_assert!(layout_engine.is_some());
+//     }
 
-    fn rect_for_text_range(
-        &self,
-        range_utf16: std::ops::Range<usize>,
-        bounds: RectF,
-        visible_bounds: RectF,
-        layout: &Self::LayoutState,
-        paint: &Self::PaintState,
-        view: &V,
-        cx: &gpui::ViewContext<V>,
-    ) -> Option<RectF> {
-        todo!("implement before merging to main")
-    }
+//     fn rect_for_text_range(
+//         &self,
+//         range_utf16: std::ops::Range<usize>,
+//         bounds: RectF,
+//         visible_bounds: RectF,
+//         layout: &Self::LayoutState,
+//         paint: &Self::PaintState,
+//         view: &V,
+//         cx: &gpui::ViewContext<V>,
+//     ) -> Option<RectF> {
+//         todo!("implement before merging to main")
+//     }
 
-    fn debug(
-        &self,
-        bounds: RectF,
-        layout: &Self::LayoutState,
-        paint: &Self::PaintState,
-        view: &V,
-        cx: &gpui::ViewContext<V>,
-    ) -> gpui::serde_json::Value {
-        todo!("implement before merging to main")
-    }
-}
+//     fn debug(
+//         &self,
+//         bounds: RectF,
+//         layout: &Self::LayoutState,
+//         paint: &Self::PaintState,
+//         view: &V,
+//         cx: &gpui::ViewContext<V>,
+//     ) -> gpui::serde_json::Value {
+//         todo!("implement before merging to main")
+//     }
+// }

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

@@ -1,104 +1,13 @@
-use std::cell::Cell;
-use std::{marker::PhantomData, rc::Rc};
-
-use crate::element::{AnyElement, PaintContext};
-use crate::layout_context::LayoutContext;
-use crate::style::{Style, StyleRefinement};
+use crate::{
+    element::{AnyElement, Element, Layout},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
+    style::{Style, StyleRefinement, Styleable},
+};
 use anyhow::Result;
-use derive_more::{Deref, DerefMut};
-use gpui::EngineLayout;
-use gpui::{geometry::rect::RectF, platform::MouseMovedEvent, EventContext};
-use playground_macros::styleable_helpers;
-use refineable::Refineable;
+use gpui::{platform::MouseMovedEvent, EventContext, LayoutId};
 use smallvec::SmallVec;
-use util::ResultExt;
-
-type LayoutId = gpui::LayoutId;
-
-#[derive(Deref, DerefMut)]
-pub struct Layout<V, D> {
-    id: LayoutId,
-    engine_layout: Option<EngineLayout>,
-    #[deref]
-    #[deref_mut]
-    element_data: D,
-    view_type: PhantomData<V>,
-}
-
-impl<V: 'static, D> Layout<V, D> {
-    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
-        Self {
-            id,
-            engine_layout,
-            element_data,
-            view_type: PhantomData,
-        }
-    }
-
-    pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
-        self.engine_layout(cx).bounds
-    }
-
-    pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
-        self.engine_layout(cx).order
-    }
-
-    fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
-        self.engine_layout
-            .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
-    }
-}
-
-pub trait Element<V> {
-    type Layout;
-
-    fn layout(
-        &mut self,
-        view: &mut V,
-        cx: &mut LayoutContext<V>,
-    ) -> Result<Layout<V, Self::Layout>>
-    where
-        Self: Sized;
-
-    fn paint(
-        &mut self,
-        view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
-        cx: &mut PaintContext<V>,
-    ) where
-        Self: Sized;
-
-    /// ## Helpers
-
-    fn hoverable(self) -> Hoverable<V, Self>
-    where
-        Self: Styleable + Sized,
-    {
-        hoverable(self)
-    }
-}
-
-pub trait Styleable {
-    type Style: refineable::Refineable;
-
-    fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
-
-    fn style(&mut self) -> playground::style::Style {
-        let mut style = playground::style::Style::default();
-        style.refine(self.declared_style());
-        style
-    }
-}
-
-// Tailwind-style helpers methods that take and return mut self
-//
-// Example:
-// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
-// fn p_2(mut self) -> Self where Self: Sized;
-use crate as playground; // Macro invocation references this crate as playground.
-pub trait StyleHelpers: Styleable<Style = Style> {
-    styleable_helpers!();
-}
+use std::rc::Rc;
 
 pub struct Div<V> {
     style: StyleRefinement,
@@ -146,66 +55,6 @@ impl<V: 'static> Element<V> for Div<V> {
     }
 }
 
-pub struct Hoverable<V, E: Element<V> + Styleable> {
-    hovered: Cell<bool>,
-    child_style: StyleRefinement,
-    hovered_style: StyleRefinement,
-    child: E,
-    view_type: PhantomData<V>,
-}
-
-pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
-    Hoverable {
-        hovered: Cell::new(false),
-        child_style: child.declared_style().clone(),
-        hovered_style: Default::default(),
-        child,
-        view_type: PhantomData,
-    }
-}
-
-impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
-    type Style = E::Style;
-
-    fn declared_style(&mut self) -> &mut playground::style::StyleRefinement {
-        self.child.declared_style()
-    }
-}
-
-impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
-    type Layout = E::Layout;
-
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
-    where
-        Self: Sized,
-    {
-        if self.hovered.get() {
-            // If hovered, refine the child's style with this element's style.
-            self.child.declared_style().refine(&self.hovered_style);
-        } else {
-            // Otherwise, set the child's style back to its original style.
-            *self.child.declared_style() = self.child_style.clone();
-        }
-
-        self.child.layout(view, cx)
-    }
-
-    fn paint(
-        &mut self,
-        view: &mut V,
-        layout: &mut Layout<V, Self::Layout>,
-        cx: &mut PaintContext<V>,
-    ) where
-        Self: Sized,
-    {
-        let bounds = layout.bounds(cx);
-        let order = layout.order(cx);
-        self.hovered.set(bounds.contains_point(cx.mouse_position()));
-        let hovered = self.hovered.clone();
-        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
-    }
-}
-
 pub trait Interactive<V> {
     fn declared_interactions(&mut self) -> &mut Interactions<V>;
 

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

@@ -1,552 +1,143 @@
-use crate::{
-    adapter::Adapter,
-    color::Hsla,
-    style::{Display, Fill, Overflow, Position, StyleRefinement},
-};
 use anyhow::Result;
-pub use gpui::LayoutContext;
-use gpui::{
-    geometry::PointRefinement,
-    platform::{MouseButton, MouseButtonEvent},
-    EngineLayout, EventContext, RenderContext, ViewContext,
-};
-use refineable::Refineable;
-use std::{
-    any::{Any, TypeId},
-    cell::Cell,
-    rc::Rc,
-};
-
-pub use crate::paint_context::PaintContext;
-pub use taffy::tree::NodeId;
-
-pub struct Layout<'a, E: ?Sized> {
-    pub from_engine: EngineLayout,
-    pub from_element: &'a mut E,
-}
-
-pub struct ElementMetadata<V> {
-    pub style: StyleRefinement,
-    pub handlers: Vec<EventHandler<V>>,
+use derive_more::{Deref, DerefMut};
+use gpui::geometry::rect::RectF;
+use gpui::EngineLayout;
+use std::marker::PhantomData;
+use util::ResultExt;
+
+use crate::layout_context::LayoutContext;
+use crate::paint_context::PaintContext;
+
+type LayoutId = gpui::LayoutId;
+
+#[derive(Deref, DerefMut)]
+pub struct Layout<V, D> {
+    id: LayoutId,
+    engine_layout: Option<EngineLayout>,
+    #[deref]
+    #[deref_mut]
+    element_data: D,
+    view_type: PhantomData<V>,
 }
 
-pub struct EventHandler<V> {
-    handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
-    event_type: TypeId,
-    outside_bounds: bool,
-}
-
-impl<V> Clone for EventHandler<V> {
-    fn clone(&self) -> Self {
+impl<V: 'static, D> Layout<V, D> {
+    pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
         Self {
-            handler: self.handler.clone(),
-            event_type: self.event_type,
-            outside_bounds: self.outside_bounds,
+            id,
+            engine_layout,
+            element_data,
+            view_type: PhantomData,
         }
     }
-}
 
-impl<V> Default for ElementMetadata<V> {
-    fn default() -> Self {
-        Self {
-            style: StyleRefinement::default(),
-            handlers: Vec::new(),
-        }
+    pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
+        self.engine_layout(cx).bounds
     }
-}
-
-pub trait Element<V: 'static>: 'static {
-    type Layout: 'static;
 
-    fn declared_style(&mut self) -> &mut StyleRefinement;
-
-    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
-        self.declared_style()
+    pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
+        self.engine_layout(cx).order
     }
 
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
+    fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
+        self.engine_layout
+            .get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
+    }
+}
 
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
-        -> Result<(NodeId, Self::Layout)>;
+pub trait Element<V> {
+    type Layout;
 
-    fn paint<'a>(
+    fn layout(
         &mut self,
-        layout: Layout<Self::Layout>,
         view: &mut V,
-        cx: &mut PaintContext<V>,
-    ) -> Result<()>;
-
-    /// Convert to a dynamically-typed element suitable for layout and paint.
-    fn into_any(self) -> AnyElement<V>
-    where
-        Self: 'static + Sized,
-    {
-        AnyElement {
-            element: Box::new(self) as Box<dyn ElementObject<V>>,
-            layout: None,
-        }
-    }
-
-    fn adapt(self) -> Adapter<V>
-    where
-        Self: Sized,
-        Self: Element<V>,
-    {
-        Adapter(self.into_any())
-    }
-
-    fn click(
-        self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseButtonEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        let pressed: Rc<Cell<bool>> = Default::default();
-        self.mouse_down(button, {
-            let pressed = pressed.clone();
-            move |_, _, _| {
-                pressed.set(true);
-            }
-        })
-        .mouse_up_outside(button, {
-            let pressed = pressed.clone();
-            move |_, _, _| {
-                pressed.set(false);
-            }
-        })
-        .mouse_up(button, move |view, event, event_cx| {
-            if pressed.get() {
-                pressed.set(false);
-                handler(view, event, event_cx);
-            }
-        })
-    }
-
-    fn mouse_down(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.handlers_mut().push(EventHandler {
-            handler: Rc::new(move |view, event, event_cx| {
-                let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
-                if event.button == button && event.is_down {
-                    handler(view, event, event_cx);
-                }
-            }),
-            event_type: TypeId::of::<MouseButtonEvent>(),
-            outside_bounds: false,
-        });
-        self
-    }
-
-    fn mouse_down_outside(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.handlers_mut().push(EventHandler {
-            handler: Rc::new(move |view, event, event_cx| {
-                let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
-                if event.button == button && event.is_down {
-                    handler(view, event, event_cx);
-                }
-            }),
-            event_type: TypeId::of::<MouseButtonEvent>(),
-            outside_bounds: true,
-        });
-        self
-    }
-
-    fn mouse_up(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.handlers_mut().push(EventHandler {
-            handler: Rc::new(move |view, event, event_cx| {
-                let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
-                if event.button == button && !event.is_down {
-                    handler(view, event, event_cx);
-                }
-            }),
-            event_type: TypeId::of::<MouseButtonEvent>(),
-            outside_bounds: false,
-        });
-        self
-    }
-
-    fn mouse_up_outside(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.handlers_mut().push(EventHandler {
-            handler: Rc::new(move |view, event, event_cx| {
-                let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
-                if event.button == button && !event.is_down {
-                    handler(view, event, event_cx);
-                }
-            }),
-            event_type: TypeId::of::<MouseButtonEvent>(),
-            outside_bounds: true,
-        });
-        self
-    }
-
-    // Display ////////////////////
-
-    fn block(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().display = Some(Display::Block);
-        self
-    }
-
-    fn flex(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().display = Some(Display::Flex);
-        self
-    }
-
-    fn grid(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().display = Some(Display::Grid);
-        self
-    }
-
-    // style::Overflow ///////////////////
-
-    fn overflow_visible(mut self) -> Self
+        cx: &mut LayoutContext<V>,
+    ) -> Result<Layout<V, Self::Layout>>
     where
-        Self: Sized,
-    {
-        self.declared_style().overflow = PointRefinement {
-            x: Some(Overflow::Visible),
-            y: Some(Overflow::Visible),
-        };
-        self
-    }
+        Self: Sized;
 
-    fn overflow_hidden(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow = PointRefinement {
-            x: Some(Overflow::Hidden),
-            y: Some(Overflow::Hidden),
-        };
-        self
-    }
+    fn paint(
+        &mut self,
+        view: &mut V,
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
+        Self: Sized;
 
-    fn overflow_scroll(mut self) -> Self
+    fn into_any(mut self) -> AnyElement<V>
     where
         Self: Sized,
     {
-        self.declared_style().overflow = PointRefinement {
-            x: Some(Overflow::Scroll),
-            y: Some(Overflow::Scroll),
-        };
-        self
+        AnyElement(Box::new(ElementWithLayout {
+            element: self,
+            layout: None,
+        }))
     }
+}
 
-    fn overflow_x_visible(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.x = Some(Overflow::Visible);
-        self
-    }
+trait ElementTraitObject<V> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
+    fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>);
+}
 
-    fn overflow_x_hidden(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.x = Some(Overflow::Hidden);
-        self
-    }
+struct ElementWithLayout<V, E: Element<V>> {
+    element: E,
+    layout: Option<Layout<V, E::Layout>>,
+}
 
-    fn overflow_x_scroll(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.x = Some(Overflow::Scroll);
-        self
+impl<V, E: Element<V>> ElementTraitObject<V> for ElementWithLayout<V, E> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+        let layout = Element::layout(self, view, cx)?;
+        let layout_id = layout.id;
+        self.layout = Some(layout);
+        Ok(layout_id)
     }
 
-    fn overflow_y_visible(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.y = Some(Overflow::Visible);
-        self
+    fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
+        let layout = self.layout.as_mut().expect("paint called before layout");
+        Element::paint(self, view, layout, cx);
     }
+}
 
-    fn overflow_y_hidden(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.y = Some(Overflow::Hidden);
-        self
-    }
+pub struct AnyElement<V>(Box<dyn ElementTraitObject<V>>);
 
-    fn overflow_y_scroll(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().overflow.y = Some(Overflow::Scroll);
-        self
+impl<V> AnyElement<V> {
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
+        self.0.layout(view, cx)
     }
 
-    // Position ///////////////////
-
-    fn relative(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().position = Some(Position::Relative);
-        self
+    fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
+        self.0.paint(view, layout_id, cx)
     }
+}
 
-    fn absolute(mut self) -> Self
-    where
-        Self: Sized,
-    {
-        self.declared_style().position = Some(Position::Absolute);
-
-        self
-    }
+pub trait ParentElement<V> {
+    fn children_mut(&mut self) -> &mut Vec<AnyElement<V>>;
 
-    fn fill(mut self, fill: impl Into<Fill>) -> Self
+    fn child(mut self, child: impl IntoElement<V>) -> Self
     where
         Self: Sized,
     {
-        self.declared_style().fill = Some(fill.into());
+        self.children_mut().push(child.into_element().into_any());
         self
     }
 
-    fn text_color(mut self, color: impl Into<Hsla>) -> Self
+    fn children<I, E>(mut self, children: I) -> Self
     where
+        I: IntoIterator<Item = E>,
+        E: IntoElement<V>,
         Self: Sized,
     {
-        self.declared_style().text_color = Some(color.into());
+        self.children_mut().extend(
+            children
+                .into_iter()
+                .map(|child| child.into_element().into_any()),
+        );
         self
     }
 }
 
-pub trait ParentElement<V: 'static>: Element<V> {
-    fn child(self, child: impl IntoElement<V>) -> Self
-    where
-        Self: Sized;
-
-    fn children<I, E>(self, children: I) -> Self
-    where
-        Self: Sized,
-        I: IntoIterator<Item = E>,
-        E: IntoElement<V>;
-}
-
-// Object-safe counterpart of Element used by AnyElement to store elements as trait objects.
-trait ElementObject<V> {
-    fn declared_style(&mut self) -> &mut StyleRefinement;
-    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement;
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
-    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
-        -> Result<(NodeId, Box<dyn Any>)>;
-    fn paint(
-        &mut self,
-        layout: Layout<dyn Any>,
-        view: &mut V,
-        cx: &mut PaintContext<V>,
-    ) -> Result<()>;
-}
-
-impl<V: 'static, E: Element<V>> ElementObject<V> for E {
-    fn declared_style(&mut self) -> &mut StyleRefinement {
-        Element::declared_style(self)
-    }
-
-    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
-        Element::computed_style(self, cx)
-    }
-
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
-        Element::handlers_mut(self)
-    }
-
-    fn layout(
-        &mut self,
-        view: &mut V,
-        cx: &mut LayoutContext<V>,
-    ) -> Result<(NodeId, Box<dyn Any>)> {
-        let (node_id, layout) = self.layout(view, cx)?;
-        let layout = Box::new(layout) as Box<dyn Any>;
-        Ok((node_id, layout))
-    }
-
-    fn paint(
-        &mut self,
-        layout: Layout<dyn Any>,
-        view: &mut V,
-        cx: &mut PaintContext<V>,
-    ) -> Result<()> {
-        let layout = Layout {
-            from_engine: layout.from_engine,
-            from_element: layout.from_element.downcast_mut::<E::Layout>().unwrap(),
-        };
-
-        self.paint(layout, view, cx)
-    }
-}
-
-/// A dynamically typed element.
-pub struct AnyElement<V> {
-    element: Box<dyn ElementObject<V>>,
-    layout: Option<(NodeId, Box<dyn Any>)>,
-}
-
-impl<V: 'static> AnyElement<V> {
-    pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<NodeId> {
-        let pushed_text_style = self.push_text_style(cx);
-
-        let (node_id, layout) = self.element.layout(view, cx)?;
-        self.layout = Some((node_id, layout));
-
-        if pushed_text_style {
-            cx.pop_text_style();
-        }
-
-        Ok(node_id)
-    }
-
-    pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
-        let pushed_text_style = self.push_text_style(cx);
-
-        let (layout_node_id, element_layout) =
-            self.layout.as_mut().expect("paint called before layout");
-
-        let layout = Layout {
-            from_engine: cx
-                .layout_engine()
-                .unwrap()
-                .computed_layout(*layout_node_id)
-                .expect("you can currently only use playground elements within an adapter"),
-            from_element: element_layout.as_mut(),
-        };
-
-        let style = self.element.computed_style(cx.as_view_context());
-
-        let fill_color = style.fill.as_ref().and_then(|fill| fill.color());
-        if let Some(fill_color) = fill_color {
-            cx.scene.push_quad(gpui::scene::Quad {
-                bounds: layout.from_engine.bounds,
-                background: Some(fill_color.into()),
-                border: Default::default(),
-                corner_radii: Default::default(),
-            });
-        }
-
-        for event_handler in self.element.handlers_mut().iter().cloned() {
-            let EngineLayout { order, bounds } = layout.from_engine;
-
-            let view_id = cx.view_id();
-            let view_event_handler = event_handler.handler.clone();
-
-            // TODO: Tuck this into a method on PaintContext.
-            cx.scene
-                .interactive_regions
-                .push(gpui::scene::InteractiveRegion {
-                    order,
-                    bounds,
-                    outside_bounds: event_handler.outside_bounds,
-                    event_handler: Rc::new(move |view, event, window_cx, view_id| {
-                        let mut view_context = ViewContext::mutable(window_cx, view_id);
-                        let mut event_context = EventContext::new(&mut view_context);
-                        view_event_handler(view.downcast_mut().unwrap(), event, &mut event_context);
-                    }),
-                    event_type: event_handler.event_type,
-                    view_id,
-                });
-        }
-
-        self.element.paint(layout, view, cx)?;
-        if pushed_text_style {
-            cx.pop_text_style();
-        }
-
-        Ok(())
-    }
-
-    fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
-        let text_style = self
-            .element
-            .computed_style(cx.as_view_context())
-            .text_style();
-        if let Some(text_style) = text_style {
-            cx.push_text_style(cx.text_style().refined(&text_style));
-            true
-        } else {
-            false
-        }
-    }
-}
-
-impl<V: 'static> Element<V> for AnyElement<V> {
-    type Layout = ();
-
-    fn declared_style(&mut self) -> &mut StyleRefinement {
-        self.element.declared_style()
-    }
-
-    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
-        self.element.computed_style(cx)
-    }
-
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
-        self.element.handlers_mut()
-    }
-
-    fn layout(
-        &mut self,
-        view: &mut V,
-        cx: &mut LayoutContext<V>,
-    ) -> Result<(NodeId, Self::Layout)> {
-        Ok((self.layout(view, cx)?, ()))
-    }
-
-    fn paint(&mut self, layout: Layout<()>, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
-        self.paint(view, cx)
-    }
-}
-
-pub trait IntoElement<V: 'static> {
+pub trait IntoElement<V> {
     type Element: Element<V>;
 
     fn into_element(self) -> Self::Element;
-
-    fn into_any_element(self) -> AnyElement<V>
-    where
-        Self: Sized,
-    {
-        self.into_element().into_any()
-    }
 }

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

@@ -1,84 +0,0 @@
-use crate::{
-    element::{
-        AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId,
-        PaintContext, ParentElement,
-    },
-    style::{Style, StyleRefinement},
-};
-use anyhow::{anyhow, Result};
-use gpui::LayoutId;
-use playground_macros::IntoElement;
-use refineable::Refineable;
-
-#[derive(IntoElement)]
-#[element_crate = "crate"]
-pub struct Frame<V: 'static> {
-    style: StyleRefinement,
-    handlers: Vec<EventHandler<V>>,
-    children: Vec<AnyElement<V>>,
-}
-
-pub fn frame<V>() -> Frame<V> {
-    Frame {
-        style: StyleRefinement::default(),
-        handlers: Vec::new(),
-        children: Vec::new(),
-    }
-}
-
-impl<V: 'static> Element<V> for Frame<V> {
-    type Layout = ();
-
-    fn declared_style(&mut self) -> &mut StyleRefinement {
-        &mut self.style
-    }
-
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
-        &mut self.handlers
-    }
-
-    fn layout(
-        &mut self,
-        view: &mut V,
-        cx: &mut LayoutContext<V>,
-    ) -> Result<(NodeId, Self::Layout)> {
-        let child_layout_node_ids = self
-            .children
-            .iter_mut()
-            .map(|child| child.layout(view, cx))
-            .collect::<Result<Vec<LayoutId>>>()?;
-
-        let rem_size = cx.rem_pixels();
-        let style = Style::default().refined(&self.style);
-        let node_id = cx
-            .layout_engine()
-            .ok_or_else(|| anyhow!("no layout engine"))?
-            .add_node(style.to_taffy(rem_size), child_layout_node_ids)?;
-
-        Ok((node_id, ()))
-    }
-
-    fn paint(&mut self, layout: Layout<()>, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
-        for child in &mut self.children {
-            child.paint(view, cx)?;
-        }
-        Ok(())
-    }
-}
-
-impl<V: 'static> ParentElement<V> for Frame<V> {
-    fn child(mut self, child: impl IntoElement<V>) -> Self {
-        self.children.push(child.into_any_element());
-        self
-    }
-
-    fn children<I, E>(mut self, children: I) -> Self
-    where
-        I: IntoIterator<Item = E>,
-        E: IntoElement<V>,
-    {
-        self.children
-            .extend(children.into_iter().map(|e| e.into_any_element()));
-        self
-    }
-}

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

@@ -1,119 +1,70 @@
-use std::{cell::Cell, marker::PhantomData, rc::Rc};
-
-use gpui::{
-    geometry::{rect::RectF, vector::Vector2F},
-    platform::MouseMovedEvent,
-    EngineLayout, ViewContext,
-};
-use refineable::Refineable;
-
 use crate::{
-    element::{Element, ParentElement},
-    style::StyleRefinement,
+    element::{Element, Layout},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
+    style::{StyleRefinement, Styleable},
 };
+use anyhow::Result;
+use gpui::platform::MouseMovedEvent;
+use refineable::Refineable;
+use std::{cell::Cell, marker::PhantomData};
 
-pub struct Hoverable<V, E> {
-    hover_style: StyleRefinement,
-    computed_style: Option<StyleRefinement>,
-    hovered: Rc<Cell<bool>>,
-    view_type: PhantomData<V>,
+pub struct Hoverable<V, E: Element<V> + Styleable> {
+    hovered: Cell<bool>,
+    child_style: StyleRefinement,
+    hovered_style: StyleRefinement,
     child: E,
+    view_type: PhantomData<V>,
 }
 
-impl<V, E> Hoverable<V, E> {
-    pub fn new(child: E) -> Self {
-        Self {
-            hover_style: StyleRefinement::default(),
-            computed_style: None,
-            hovered: Default::default(),
-            view_type: PhantomData,
-            child,
-        }
+pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
+    Hoverable {
+        hovered: Cell::new(false),
+        child_style: child.declared_style().clone(),
+        hovered_style: Default::default(),
+        child,
+        view_type: PhantomData,
     }
 }
 
-impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
-    type Layout = E::Layout;
+impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
+    type Style = E::Style;
 
-    fn declared_style(&mut self) -> &mut StyleRefinement {
-        &mut self.hover_style
+    fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
+        self.child.declared_style()
     }
+}
 
-    fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
-        dbg!(self.computed_style.is_some());
-
-        self.computed_style.get_or_insert_with(|| {
-            let mut style = self.child.computed_style(cx).clone();
-            if self.hovered.get() {
-                style.refine(&self.hover_style);
-            }
-            style
-        })
-    }
+impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
+    type Layout = E::Layout;
 
-    fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
-        self.child.handlers_mut()
-    }
+    fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
+    where
+        Self: Sized,
+    {
+        if self.hovered.get() {
+            // If hovered, refine the child's style with this element's style.
+            self.child.declared_style().refine(&self.hovered_style);
+        } else {
+            // Otherwise, set the child's style back to its original style.
+            *self.child.declared_style() = self.child_style.clone();
+        }
 
-    fn layout(
-        &mut self,
-        view: &mut V,
-        cx: &mut gpui::LayoutContext<V>,
-    ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
         self.child.layout(view, cx)
     }
 
-    fn paint<'a>(
+    fn paint(
         &mut self,
-        layout: crate::element::Layout<Self::Layout>,
         view: &mut V,
-        cx: &mut crate::element::PaintContext<V>,
-    ) -> anyhow::Result<()> {
-        let EngineLayout { bounds, order } = layout.from_engine;
-        let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
-        let hovered = self.hovered.clone();
-
-        self.child.paint(layout, view, cx)?;
-
-        let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
-        if mouse_within_bounds != hovered.get() {
-            hovered.set(mouse_within_bounds);
-            cx.repaint();
-        }
-
-        cx.draw_interactive_region(
-            order,
-            window_bounds,
-            false,
-            move |view, event: &MouseMovedEvent, cx| {
-                let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
-                if mouse_within_bounds != hovered.get() {
-                    dbg!("hovered", mouse_within_bounds);
-                    hovered.set(mouse_within_bounds);
-                    cx.repaint();
-                }
-            },
-        );
-        Ok(())
-    }
-}
-
-impl<V: 'static, P: ParentElement<V>> ParentElement<V> for Hoverable<V, P> {
-    fn child(mut self, child: impl crate::element::IntoElement<V>) -> Self
-    where
-        Self: Sized,
-    {
-        self.child = self.child.child(child);
-        self
-    }
-
-    fn children<I, E>(mut self, children: I) -> Self
-    where
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) where
         Self: Sized,
-        I: IntoIterator<Item = E>,
-        E: crate::element::IntoElement<V>,
     {
-        self.child = self.child.children(children);
-        self
+        let bounds = layout.bounds(cx);
+        let order = layout.order(cx);
+        self.hovered.set(bounds.contains_point(cx.mouse_position()));
+        let hovered = self.hovered.clone();
+        cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
     }
 }

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

@@ -4,7 +4,7 @@ pub use gpui::LayoutContext as LegacyLayoutContext;
 use gpui::{RenderContext, ViewContext};
 pub use taffy::tree::NodeId;
 
-use crate::{div::Layout, style::Style};
+use crate::{element::Layout, style::Style};
 
 #[derive(Deref, DerefMut)]
 pub struct LayoutContext<'a, 'b, 'c, 'd, V> {

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

@@ -1,7 +1,7 @@
 use crate::{
     color::Hsla,
-    div::{Element, Layout},
-    element::PaintContext,
+    element::{Element, Layout},
+    paint_context::PaintContext,
 };
 use gpui::{
     fonts::TextStyleRefinement,
@@ -10,6 +10,7 @@ use gpui::{
         Size, SizeRefinement,
     },
 };
+use playground_macros::styleable_helpers;
 use refineable::Refineable;
 pub use taffy::style::{
     AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
@@ -243,3 +244,25 @@ impl CornerRadii {
         }
     }
 }
+
+pub trait Styleable {
+    type Style: refineable::Refineable;
+
+    fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
+
+    fn style(&mut self) -> playground::style::Style {
+        let mut style = playground::style::Style::default();
+        style.refine(self.declared_style());
+        style
+    }
+}
+
+// Tailwind-style helpers methods that take and return mut self
+//
+// Example:
+// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
+// fn p_2(mut self) -> Self where Self: Sized;
+use crate as playground; // Macro invocation references this crate as playground.
+pub trait StyleHelpers: Styleable<Style = Style> {
+    styleable_helpers!();
+}

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

@@ -1,40 +1,35 @@
 use crate::{
-    element::{Element, ElementMetadata, EventHandler, IntoElement},
+    element::{Element, IntoElement, Layout},
+    layout_context::LayoutContext,
+    paint_context::PaintContext,
     style::Style,
 };
+use anyhow::Result;
 use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
 use parking_lot::Mutex;
 use refineable::Refineable;
 use std::sync::Arc;
 
 impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
-    type Element = Text<V>;
+    type Element = Text;
 
     fn into_element(self) -> Self::Element {
-        Text {
-            text: self.into(),
-            metadata: Default::default(),
-        }
+        Text { text: self.into() }
     }
 }
 
-pub struct Text<V> {
+pub struct Text {
     text: ArcCow<'static, str>,
-    metadata: ElementMetadata<V>,
 }
 
-impl<V: 'static> Element<V> for Text<V> {
+impl<V: 'static> Element<V> for Text {
     type Layout = Arc<Mutex<Option<TextLayout>>>;
 
-    fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
-        &mut self.metadata.style
-    }
-
     fn layout(
         &mut self,
         view: &mut V,
-        cx: &mut gpui::LayoutContext<V>,
-    ) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
+        cx: &mut LayoutContext<V>,
+    ) -> Result<Layout<V, Self::Layout>> {
         let rem_size = cx.rem_pixels();
         let fonts = cx.platform().fonts();
         let text_style = cx.text_style();
@@ -72,10 +67,10 @@ impl<V: 'static> Element<V> for Text<V> {
 
     fn paint<'a>(
         &mut self,
-        layout: crate::element::Layout<Arc<Mutex<Option<TextLayout>>>>,
         view: &mut V,
-        cx: &mut crate::element::PaintContext<V>,
-    ) -> anyhow::Result<()> {
+        layout: &mut Layout<V, Self::Layout>,
+        cx: &mut PaintContext<V>,
+    ) {
         let element_layout_lock = layout.from_element.lock();
         let element_layout = element_layout_lock
             .as_ref()
@@ -94,11 +89,6 @@ impl<V: 'static> Element<V> for Text<V> {
             line_height,
             cx.legacy_cx,
         );
-        Ok(())
-    }
-
-    fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
-        &mut self.metadata.handlers
     }
 }
 

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

@@ -1,5 +1,5 @@
 use crate::element::{AnyElement, Element};
-use gpui::{Element as _, ViewContext};
+use gpui::ViewContext;
 
 pub fn view<F, E>(mut render: F) -> ViewFn
 where