WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/element.rs               |  16 +-
crates/gpui3/src/elements/clickable.rs    |  25 ++-
crates/gpui3/src/elements/div.rs          |  28 +++
crates/gpui3/src/elements/hoverable.rs    |  25 ++-
crates/gpui3/src/elements/img.rs          | 108 +++++++++++----
crates/gpui3/src/elements/layout_node.rs  |  40 +----
crates/gpui3/src/elements/pressable.rs    | 167 -------------------------
crates/gpui3/src/elements/svg.rs          | 105 +++++++++++----
crates/gpui3_macros/src/derive_element.rs |   3 
crates/ui2/src/components/notification.rs |   2 
crates/ui2/src/components/panel.rs        |   4 
crates/ui2/src/components/panes.rs        |   4 
crates/ui2/src/components/toast.rs        |   4 
crates/ui2/src/elements/input.rs          |  16 +
crates/ui2/src/elements/stack.rs          |   2 
crates/ui2/src/prelude.rs                 |   4 
16 files changed, 251 insertions(+), 302 deletions(-)

Detailed changes

crates/gpui3/src/element.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext};
+use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, SharedString, ViewContext};
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 
@@ -33,12 +33,11 @@ pub trait IdentifiedElement: Element {
 #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
 pub(crate) struct GlobalElementId(SmallVec<[ElementId; 8]>);
 
-pub trait ParentElement {
-    type State;
+pub trait ParentElement: Element {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]>;
+    fn group_mut(&mut self) -> &mut Option<SharedString>;
 
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]>;
-
-    fn child(mut self, child: impl IntoAnyElement<Self::State>) -> Self
+    fn child(mut self, child: impl IntoAnyElement<Self::ViewState>) -> Self
     where
         Self: Sized,
     {
@@ -46,7 +45,10 @@ pub trait ParentElement {
         self
     }
 
-    fn children(mut self, iter: impl IntoIterator<Item = impl IntoAnyElement<Self::State>>) -> Self
+    fn children(
+        mut self,
+        iter: impl IntoIterator<Item = impl IntoAnyElement<Self::ViewState>>,
+    ) -> Self
     where
         Self: Sized,
     {

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

@@ -1,7 +1,7 @@
 use crate::{
-    AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind, Hoverable,
-    IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseDownEvent, MouseUpEvent, Pixels,
-    SharedString, StyleRefinement, Styled, ViewContext,
+    AnyElement, Bounds, DispatchPhase, Element, ElementId, Hoverable, IdentifiedElement,
+    IntoAnyElement, LayoutId, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, SharedString,
+    StyleRefinement, Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use refineable::CascadeSlot;
@@ -53,7 +53,17 @@ pub struct ClickableElement<E: Element> {
     cascade_slot: CascadeSlot,
 }
 
-impl<E: Element> ClickableElement<E> {
+impl<E: Styled + Element> ClickableElement<E> {
+    pub fn new(mut child: E) -> Self {
+        let cascade_slot = child.style_cascade().reserve();
+        ClickableElement {
+            child,
+            listeners: Default::default(),
+            active_style: Default::default(),
+            cascade_slot,
+        }
+    }
+
     pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
         self,
         replace: impl FnOnce(E) -> E2,
@@ -171,12 +181,11 @@ where
 
 impl<E: Styled + IdentifiedElement> IdentifiedElement for ClickableElement<E> {}
 
-impl<E, K> LayoutNode<E::ViewState, K> for ClickableElement<E>
+impl<E> ParentElement for ClickableElement<E>
 where
-    E: Element + LayoutNode<E::ViewState, K>,
-    K: ElementKind,
+    E: Styled + ParentElement,
 {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<E::ViewState>; 2]> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
         self.child.children_mut()
     }
 

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

@@ -1,8 +1,9 @@
 use crate::{
-    AnonymousElementKind, AnyElement, Bounds, Clickable, ClickableElement, ClickableElementState,
-    Element, ElementId, ElementKind, Hoverable, HoverableElement, IdentifiedElementKind,
-    IntoAnyElement, LayoutId, LayoutNode, LayoutNodeElement, Overflow, Pixels, Point, SharedString,
-    Style, StyleCascade, StyleRefinement, Styled, ViewContext, ClickListeners,
+    AnonymousElementKind, AnyElement, Bounds, ClickListeners, Clickable, ClickableElement,
+    ClickableElementState, Element, ElementId, ElementKind, Hoverable, HoverableElement,
+    IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, LayoutNodeElement,
+    Overflow, ParentElement, Pixels, Point, SharedString, Style, StyleCascade, StyleRefinement,
+    Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use smallvec::SmallVec;
@@ -29,7 +30,16 @@ impl ScrollState {
     }
 }
 
-pub struct Div<V: 'static + Send + Sync, K: ElementKind>(
+pub fn div<S>() -> Div<S, AnonymousElementKind>
+where
+    S: 'static + Send + Sync,
+{
+    Div(ClickableElement::new(HoverableElement::new(
+        LayoutNodeElement::new(),
+    )))
+}
+
+pub struct Div<V: 'static + Send + Sync, K: ElementKind = AnonymousElementKind>(
     ClickableElement<HoverableElement<LayoutNodeElement<V, K>>>,
 );
 
@@ -95,7 +105,13 @@ impl<V: 'static + Send + Sync> Div<V, AnonymousElementKind> {
     }
 }
 
-impl<V: 'static + Send + Sync, K: ElementKind> LayoutNode<V, K> for Div<V, K> {
+impl<V: 'static + Send + Sync> IdentifiedElement for Div<V, IdentifiedElementKind> {
+    fn id(&self) -> ElementId {
+        IdentifiedElement::id(&self.0)
+    }
+}
+
+impl<V: 'static + Send + Sync, K: ElementKind> ParentElement for Div<V, K> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         self.0.children_mut()
     }

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

@@ -1,7 +1,7 @@
 use crate::{
-    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind,
-    IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseMoveEvent, Pixels, SharedString,
-    Style, StyleCascade, StyleRefinement, Styled, ViewContext,
+    group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement,
+    IntoAnyElement, LayoutId, MouseMoveEvent, ParentElement, Pixels, SharedString, Style,
+    StyleCascade, StyleRefinement, Styled, ViewContext,
 };
 use refineable::CascadeSlot;
 use std::sync::{
@@ -30,6 +30,17 @@ pub struct HoverableElement<E> {
 }
 
 impl<E: Styled + Element> HoverableElement<E> {
+    pub fn new(mut child: E) -> Self {
+        let cascade_slot = child.style_cascade().reserve();
+        HoverableElement {
+            hover_style: StyleRefinement::default(),
+            group: None,
+            cascade_slot,
+            hovered: Arc::new(AtomicBool::new(false)),
+            child,
+        }
+    }
+
     pub fn replace_child<E2: Element<ViewState = E::ViewState>>(
         self,
         replace: impl FnOnce(E) -> E2,
@@ -106,13 +117,11 @@ where
     }
 }
 
-impl<E, K, V> LayoutNode<V, K> for HoverableElement<E>
+impl<E> ParentElement for HoverableElement<E>
 where
-    E: LayoutNode<V, K>,
-    K: ElementKind,
-    V: 'static + Send + Sync,
+    E: Styled + ParentElement,
 {
-    fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<V>; 2]> {
+    fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement<E::ViewState>; 2]> {
         self.child.children_mut()
     }
 

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

@@ -1,29 +1,35 @@
 use crate::{
-    AnyElement, BorrowWindow, Bounds, Element, IntoAnyElement, LayoutId, Pixels, SharedString,
-    Style, Styled, ViewContext,
+    AnonymousElementKind, AnyElement, BorrowWindow, Bounds, ClickListeners, Clickable,
+    ClickableElement, ClickableElementState, Element, ElementId, ElementKind, Hoverable,
+    HoverableElement, IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId,
+    LayoutNodeElement, Pixels, SharedString, Style, StyleRefinement, Styled, ViewContext,
 };
 use futures::FutureExt;
 use refineable::Cascade;
-use std::marker::PhantomData;
 use util::ResultExt;
 
-pub struct Img<S> {
-    style: Cascade<Style>,
+pub struct Img<V: 'static + Send + Sync, K: ElementKind = AnonymousElementKind> {
+    layout_node: ClickableElement<HoverableElement<LayoutNodeElement<V, K>>>,
     uri: Option<SharedString>,
     grayscale: bool,
-    state_type: PhantomData<S>,
 }
 
-pub fn img<S>() -> Img<S> {
+pub fn img<V>() -> Img<V, AnonymousElementKind>
+where
+    V: 'static + Send + Sync,
+{
     Img {
-        style: Cascade::default(),
+        layout_node: ClickableElement::new(HoverableElement::new(LayoutNodeElement::new())),
         uri: None,
         grayscale: false,
-        state_type: PhantomData,
     }
 }
 
-impl<S> Img<S> {
+impl<V, K> Img<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
     pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
         self.uri = Some(uri.into());
         self
@@ -35,47 +41,65 @@ impl<S> Img<S> {
     }
 }
 
-impl<S> IntoAnyElement<S> for Img<S>
+impl<V: 'static + Send + Sync> Img<V, AnonymousElementKind> {
+    pub fn id(self, id: impl Into<ElementId>) -> Img<V, IdentifiedElementKind> {
+        Img {
+            layout_node: self.layout_node.replace_child(|hoverable| {
+                hoverable.replace_child(|layout_node| layout_node.identify(id))
+            }),
+            uri: self.uri,
+            grayscale: self.grayscale,
+        }
+    }
+}
+
+impl<V, K> IntoAnyElement<V> for Img<V, K>
 where
-    S: 'static + Send + Sync,
+    V: 'static + Send + Sync,
+    K: ElementKind,
 {
-    fn into_any(self) -> AnyElement<S> {
+    fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<S: Send + Sync + 'static> Element for Img<S> {
-    type ViewState = S;
-    type ElementState = ();
+impl<V, K> Element for Img<V, K>
+where
+    V: Send + Sync + 'static,
+    K: ElementKind,
+{
+    type ViewState = V;
+    type ElementState = ClickableElementState<()>;
 
     fn id(&self) -> Option<crate::ElementId> {
-        None
+        self.layout_node.id()
     }
 
     fn layout(
         &mut self,
-        _: &mut Self::ViewState,
-        _: Option<Self::ElementState>,
+        view_state: &mut Self::ViewState,
+        element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<Self::ViewState>,
     ) -> (LayoutId, Self::ElementState)
     where
         Self: Sized,
     {
-        let style = self.computed_style();
-        let layout_id = cx.request_layout(&style, []);
-        (layout_id, ())
+        self.layout_node.layout(view_state, element_state, cx)
     }
 
     fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        _: &mut Self::ViewState,
-        _: &mut Self::ElementState,
+        view: &mut Self::ViewState,
+        element_state: &mut Self::ElementState,
         cx: &mut ViewContext<Self::ViewState>,
     ) {
+        cx.stack(1, |cx| {
+            self.layout_node.paint(bounds, view, element_state, cx);
+        });
+
         let style = self.computed_style();
         let corner_radii = style.corner_radii;
-        style.paint(bounds, cx);
 
         if let Some(uri) = self.uri.clone() {
             let image_future = cx.image_cache.get(uri);
@@ -101,14 +125,38 @@ impl<S: Send + Sync + 'static> Element for Img<S> {
     }
 }
 
-impl<S> Styled for Img<S> {
+impl<V: 'static + Send + Sync> IdentifiedElement for Img<V, IdentifiedElementKind> {
+    fn id(&self) -> ElementId {
+        IdentifiedElement::id(&self.layout_node)
+    }
+}
+
+impl<V, K> Styled for Img<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
     fn style_cascade(&mut self) -> &mut Cascade<Style> {
-        todo!("use layout node")
-        // &mut self.style
+        self.layout_node.style_cascade()
     }
 
     fn computed_style(&mut self) -> &Style {
-        todo!("use layout node")
-        // self.style.compute()
+        self.layout_node.computed_style()
+    }
+}
+
+impl<V: 'static + Send + Sync, K: ElementKind> Hoverable for Img<V, K> {
+    fn hover_style(&mut self) -> &mut StyleRefinement {
+        self.layout_node.hover_style()
+    }
+}
+
+impl<V: 'static + Send + Sync> Clickable for Img<V, IdentifiedElementKind> {
+    fn active_style(&mut self) -> &mut StyleRefinement {
+        self.layout_node.active_style()
+    }
+
+    fn listeners(&mut self) -> &mut ClickListeners<V> {
+        self.layout_node.listeners()
     }
 }

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

@@ -1,6 +1,7 @@
 use crate::{
     AnyElement, AppContext, BorrowWindow, Bounds, Element, ElementId, IdentifiedElement,
-    IntoAnyElement, LayoutId, Pixels, SharedString, Style, StyleCascade, Styled, ViewContext,
+    IntoAnyElement, LayoutId, ParentElement, Pixels, SharedString, Style, StyleCascade, Styled,
+    ViewContext,
 };
 use collections::HashMap;
 use refineable::Refineable;
@@ -16,31 +17,6 @@ pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<P
         .and_then(|bounds_stack| bounds_stack.last().cloned())
 }
 
-pub trait LayoutNode<V: 'static + Send + Sync, K: ElementKind> {
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
-    fn group_mut(&mut self) -> &mut Option<SharedString>;
-
-    fn child(mut self, child: impl IntoAnyElement<V>) -> Self
-    where
-        Self: Sized,
-    {
-        self.children_mut().push(child.into_any());
-        self
-    }
-
-    fn children<C, E>(mut self, children: C) -> Self
-    where
-        C: IntoIterator<Item = E>,
-        E: IntoAnyElement<V>,
-        Self: Sized,
-    {
-        for child in children {
-            self.children_mut().push(child.into_any());
-        }
-        self
-    }
-}
-
 pub trait ElementKind: 'static + Send + Sync {
     fn id(&self) -> Option<ElementId>;
 }
@@ -69,6 +45,16 @@ pub struct LayoutNodeElement<V: 'static + Send + Sync, K: ElementKind> {
 }
 
 impl<V: 'static + Send + Sync> LayoutNodeElement<V, AnonymousElementKind> {
+    pub fn new() -> LayoutNodeElement<V, AnonymousElementKind> {
+        LayoutNodeElement {
+            style_cascade: StyleCascade::default(),
+            computed_style: None,
+            children: SmallVec::new(),
+            kind: AnonymousElementKind,
+            group: None,
+        }
+    }
+
     pub fn identify(self, id: impl Into<ElementId>) -> LayoutNodeElement<V, IdentifiedElementKind> {
         LayoutNodeElement {
             style_cascade: self.style_cascade,
@@ -196,7 +182,7 @@ impl<V: 'static + Send + Sync, K: ElementKind> Element for LayoutNodeElement<V,
     }
 }
 
-impl<V: 'static + Send + Sync, K: ElementKind> LayoutNode<V, K> for LayoutNodeElement<V, K> {
+impl<V: 'static + Send + Sync, K: ElementKind> ParentElement for LayoutNodeElement<V, K> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         &mut self.children
     }

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

@@ -1,167 +0,0 @@
-use crate::{
-    group_bounds, AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive,
-    IntoAnyElement, MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels,
-    SharedString, Styled, ViewContext,
-};
-use refineable::{Cascade, CascadeSlot, Refineable};
-use smallvec::SmallVec;
-use std::sync::{
-    atomic::{AtomicBool, Ordering::SeqCst},
-    Arc,
-};
-
-pub struct Pressable<E: Styled> {
-    group: Option<SharedString>,
-    cascade_slot: CascadeSlot,
-    pressed_style: <E::Style as Refineable>::Refinement,
-    child: E,
-}
-
-pub struct PressableState<S> {
-    pressed: Arc<AtomicBool>,
-    child_state: S,
-}
-
-impl<E: Styled> Pressable<E> {
-    pub fn new(mut child: E, group: Option<SharedString>) -> Self {
-        Self {
-            group,
-            cascade_slot: child.style_cascade().reserve(),
-            pressed_style: Default::default(),
-            child,
-        }
-    }
-}
-
-impl<E> Styled for Pressable<E>
-where
-    E: Styled,
-{
-    type Style = E::Style;
-
-    fn style_cascade(&mut self) -> &mut Cascade<E::Style> {
-        self.child.style_cascade()
-    }
-
-    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
-        &mut self.pressed_style
-    }
-}
-
-impl<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Pressable<E> {
-    fn listeners(&mut self) -> &mut MouseEventListeners<S> {
-        self.child.listeners()
-    }
-}
-
-impl<E> IntoAnyElement<E::ViewState> for Pressable<E>
-where
-    E: Styled + IdentifiedElement,
-    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
-    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
-{
-    fn into_any(self) -> AnyElement<E::ViewState> {
-        AnyElement::new(self)
-    }
-}
-
-impl<E> Element for Pressable<E>
-where
-    E: Styled + IdentifiedElement,
-    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
-    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
-{
-    type ViewState = E::ViewState;
-    type ElementState = PressableState<E::ElementState>;
-
-    fn id(&self) -> Option<crate::ElementId> {
-        Some(IdentifiedElement::element_id(&self.child))
-    }
-
-    fn layout(
-        &mut self,
-        state: &mut Self::ViewState,
-        element_state: Option<Self::ElementState>,
-        cx: &mut ViewContext<Self::ViewState>,
-    ) -> (crate::LayoutId, Self::ElementState) {
-        if let Some(element_state) = element_state {
-            let (id, child_state) = self
-                .child
-                .layout(state, Some(element_state.child_state), cx);
-            let element_state = PressableState {
-                pressed: element_state.pressed,
-                child_state,
-            };
-            (id, element_state)
-        } else {
-            let (id, child_state) = self.child.layout(state, None, cx);
-            let element_state = PressableState {
-                pressed: Default::default(),
-                child_state,
-            };
-            (id, element_state)
-        }
-    }
-
-    fn paint(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        state: &mut Self::ViewState,
-        element_state: &mut Self::ElementState,
-        cx: &mut ViewContext<Self::ViewState>,
-    ) {
-        let target_bounds = self
-            .group
-            .as_ref()
-            .and_then(|group| group_bounds(group, cx))
-            .unwrap_or(bounds);
-
-        let style = element_state
-            .pressed
-            .load(SeqCst)
-            .then_some(self.pressed_style.clone());
-        let slot = self.cascade_slot;
-        self.style_cascade().set(slot, style);
-
-        let pressed = element_state.pressed.clone();
-        cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, cx| {
-            if phase == DispatchPhase::Bubble {
-                if target_bounds.contains_point(event.position) {
-                    pressed.store(true, SeqCst);
-                    cx.notify();
-                }
-            }
-        });
-        let pressed = element_state.pressed.clone();
-        cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
-            if phase == DispatchPhase::Capture {
-                if pressed.load(SeqCst) {
-                    pressed.store(false, SeqCst);
-                    cx.notify();
-                }
-            }
-        });
-
-        self.child
-            .paint(bounds, state, &mut element_state.child_state, cx);
-    }
-}
-
-impl<E> ParentElement for Pressable<E>
-where
-    E: ParentElement + IdentifiedElement + Styled,
-{
-    type State = E::State;
-
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
-        self.child.children_mut()
-    }
-}
-
-impl<E> IdentifiedElement for Pressable<E>
-where
-    E: IdentifiedElement + Styled,
-    <E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
-    <<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
-{
-}

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

@@ -1,70 +1,93 @@
 use crate::{
-    AnyElement, Bounds, Element, IntoAnyElement, LayoutId, Pixels, SharedString, Style, Styled,
+    AnonymousElementKind, AnyElement, Bounds, ClickListeners, Clickable, ClickableElement,
+    ClickableElementState, Element, ElementId, ElementKind, Hoverable, HoverableElement,
+    IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, LayoutNodeElement, Pixels,
+    SharedString, Style, StyleRefinement, Styled,
 };
 use refineable::Cascade;
-use std::marker::PhantomData;
 use util::ResultExt;
 
-pub struct Svg<S> {
+pub struct Svg<V: 'static + Send + Sync, K: ElementKind = AnonymousElementKind> {
+    layout_node: ClickableElement<HoverableElement<LayoutNodeElement<V, K>>>,
     path: Option<SharedString>,
-    style: Cascade<Style>,
-    state_type: PhantomData<S>,
 }
 
-pub fn svg<S>() -> Svg<S> {
+pub fn svg<V>() -> Svg<V, AnonymousElementKind>
+where
+    V: 'static + Send + Sync,
+{
     Svg {
+        layout_node: ClickableElement::new(HoverableElement::new(LayoutNodeElement::new())),
         path: None,
-        style: Cascade::<Style>::default(),
-        state_type: PhantomData,
     }
 }
 
-impl<S> Svg<S> {
+impl<V, K> Svg<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
     pub fn path(mut self, path: impl Into<SharedString>) -> Self {
         self.path = Some(path.into());
         self
     }
 }
 
-impl<S> IntoAnyElement<S> for Svg<S>
+impl<V: 'static + Send + Sync> Svg<V, AnonymousElementKind> {
+    pub fn id(self, id: impl Into<ElementId>) -> Svg<V, IdentifiedElementKind> {
+        Svg {
+            layout_node: self.layout_node.replace_child(|hoverable| {
+                hoverable.replace_child(|layout_node| layout_node.identify(id))
+            }),
+            path: self.path,
+        }
+    }
+}
+
+impl<V, K> IntoAnyElement<V> for Svg<V, K>
 where
-    S: 'static + Send + Sync,
+    V: 'static + Send + Sync,
+    K: ElementKind,
 {
-    fn into_any(self) -> AnyElement<S> {
+    fn into_any(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<S: 'static + Send + Sync> Element for Svg<S> {
-    type ViewState = S;
-    type ElementState = ();
+impl<V, K> Element for Svg<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
+    type ViewState = V;
+    type ElementState = ClickableElementState<()>;
 
     fn id(&self) -> Option<crate::ElementId> {
-        None
+        self.layout_node.id()
     }
 
     fn layout(
         &mut self,
-        _: &mut S,
-        _: Option<Self::ElementState>,
-        cx: &mut crate::ViewContext<S>,
+        view: &mut V,
+        element_state: Option<Self::ElementState>,
+        cx: &mut crate::ViewContext<V>,
     ) -> (LayoutId, Self::ElementState)
     where
         Self: Sized,
     {
-        let style = self.computed_style();
-        (cx.request_layout(&style, []), ())
+        self.layout_node.layout(view, element_state, cx)
     }
 
     fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
-        _: &mut Self::ViewState,
-        _: &mut Self::ElementState,
-        cx: &mut crate::ViewContext<S>,
+        view: &mut Self::ViewState,
+        element_state: &mut Self::ElementState,
+        cx: &mut crate::ViewContext<V>,
     ) where
         Self: Sized,
     {
+        self.layout_node.paint(bounds, view, element_state, cx);
         let fill_color = self
             .computed_style()
             .fill
@@ -76,12 +99,38 @@ impl<S: 'static + Send + Sync> Element for Svg<S> {
     }
 }
 
-impl<S: 'static + Send + Sync> Styled for Svg<S> {
-    fn style_cascade(&mut self) -> &mut crate::StyleCascade {
-        todo!("use layout node")
+impl<V: 'static + Send + Sync> IdentifiedElement for Svg<V, IdentifiedElementKind> {
+    fn id(&self) -> ElementId {
+        IdentifiedElement::id(&self.layout_node)
+    }
+}
+
+impl<V, K> Styled for Svg<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
+    fn style_cascade(&mut self) -> &mut Cascade<Style> {
+        self.layout_node.style_cascade()
     }
 
     fn computed_style(&mut self) -> &Style {
-        todo!("use layout node")
+        self.layout_node.computed_style()
+    }
+}
+
+impl<V: 'static + Send + Sync, K: ElementKind> Hoverable for Svg<V, K> {
+    fn hover_style(&mut self) -> &mut StyleRefinement {
+        self.layout_node.hover_style()
+    }
+}
+
+impl<V: 'static + Send + Sync> Clickable for Svg<V, IdentifiedElementKind> {
+    fn active_style(&mut self) -> &mut StyleRefinement {
+        self.layout_node.active_style()
+    }
+
+    fn listeners(&mut self) -> &mut ClickListeners<V> {
+        self.layout_node.listeners()
     }
 }

crates/gpui3_macros/src/derive_element.rs 🔗

@@ -53,8 +53,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
             type ViewState = #state_type;
             type ElementState = gpui3::AnyElement<#state_type>;
 
-            fn element_id(&self) -> Option<gpui3::ElementId> {
-                // todo!("What should element_id be here?")
+            fn id(&self) -> Option<gpui3::ElementId> {
                 None
             }
 

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

@@ -1,6 +1,6 @@
 use std::marker::PhantomData;
 
-use gpui3::{Element, ParentElement, StyleHelpers, ViewContext};
+use gpui3::{Element, ParentElement, ViewContext};
 
 use crate::{
     h_stack, v_stack, Button, Icon, IconButton, IconElement, Label, ThemeColor, Toast, ToastOrigin,

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

@@ -140,9 +140,7 @@ impl<S: 'static + Send + Sync> Panel<S> {
 }
 
 impl<S: 'static + Send + Sync> ParentElement for Panel<S> {
-    type State = S;
-
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
         &mut self.children
     }
 }

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

@@ -57,9 +57,7 @@ impl<S: 'static + Send + Sync> Pane<S> {
 }
 
 impl<S: 'static + Send + Sync> ParentElement for Pane<S> {
-    type State = S;
-
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
         &mut self.children
     }
 }

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

@@ -62,9 +62,7 @@ impl<S: 'static + Send + Sync> Toast<S> {
 }
 
 impl<S: 'static + Send + Sync> ParentElement for Toast<S> {
-    type State = S;
-
-    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::ViewState>; 2]> {
         &mut self.children
     }
 }

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

@@ -88,11 +88,11 @@ impl<S: 'static + Send + Sync> Input<S> {
             .border()
             .border_color(border_color_default)
             .fill(background_color_default)
-            .hover()
-            .border_color(border_color_hover)
-            // .active()
-            // .border_color(border_color_active)
-            .fill(background_color_active)
+            .hover(|h| {
+                h.border_color(border_color_hover)
+                    .fill(background_color_active)
+            })
+            // .active(|a| .border_color(border_color_active))
             .flex()
             .items_center()
             .child(
@@ -128,7 +128,11 @@ mod stories {
             }
         }
 
-        fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
+        fn render(
+            &mut self,
+            _view: &mut S,
+            cx: &mut ViewContext<S>,
+        ) -> impl Element<ViewState = S> {
             Story::container(cx)
                 .child(Story::title_for::<_, Input<S>>(cx))
                 .child(Story::label(cx, "Default"))

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

@@ -2,7 +2,7 @@ use gpui3::{div, Div};
 
 use crate::prelude::*;
 
-pub trait Stack: StyleHelpers {
+pub trait Stack: Styled + Sized {
     /// Horizontally stacks elements.
     fn h_stack(self) -> Self {
         self.flex().flex_row().items_center()

crates/ui2/src/prelude.rs 🔗

@@ -1,6 +1,6 @@
 pub use gpui3::{
-    div, Element, IntoAnyElement, ParentElement, ScrollState, StyleHelpers, Styled, ViewContext,
-    WindowContext,
+    div, Clickable, Element, Hoverable, IntoAnyElement, ParentElement, ScrollState, Styled,
+    ViewContext, WindowContext,
 };
 
 pub use crate::{theme, ButtonVariant, ElementExt, Theme};