Checkpoint

Nathan Sobo created

Change summary

crates/command_palette2/src/command_palette.rs   |  10 
crates/editor2/src/editor.rs                     |  10 
crates/editor2/src/element.rs                    |   2 
crates/editor2/src/items.rs                      |   2 
crates/go_to_line2/src/go_to_line.rs             |   8 
crates/gpui2/src/element.rs                      |  12 
crates/gpui2/src/elements.rs                     |   5 
crates/gpui2/src/elements/div.rs                 |  42 
crates/gpui2/src/elements/img.rs                 | 170 +--
crates/gpui2/src/elements/node.rs                | 217 +++-
crates/gpui2/src/elements/svg.rs                 | 125 -
crates/gpui2/src/elements/text.rs                |   2 
crates/gpui2/src/elements/uniform_list.rs        | 219 ++--
crates/gpui2/src/interactive.rs                  | 920 -----------------
crates/gpui2/src/key_dispatch.rs                 | 118 --
crates/gpui2/src/prelude.rs                      |   5 
crates/gpui2/src/styled.rs                       | 288 +----
crates/gpui2/src/view.rs                         |   2 
crates/gpui2_macros/src/style_helpers.rs         |   4 
crates/picker2/src/picker2.rs                    |   6 
crates/storybook2/src/stories/colors.rs          |   4 
crates/storybook2/src/stories/focus.rs           |  11 
crates/storybook2/src/stories/kitchen_sink.rs    |   4 
crates/storybook2/src/stories/picker.rs          |  12 
crates/storybook2/src/stories/scroll.rs          |   5 
crates/storybook2/src/stories/text.rs            |   4 
crates/storybook2/src/stories/z_index.rs         |   6 
crates/storybook2/src/storybook2.rs              |   4 
crates/theme2/src/players.rs                     |   4 
crates/theme2/src/story.rs                       |   4 
crates/ui2/src/components/avatar.rs              |   4 
crates/ui2/src/components/button.rs              |   6 
crates/ui2/src/components/checkbox.rs            |  10 
crates/ui2/src/components/context_menu.rs        |   4 
crates/ui2/src/components/details.rs             |   4 
crates/ui2/src/components/elevated_surface.rs    |   6 
crates/ui2/src/components/facepile.rs            |   4 
crates/ui2/src/components/icon.rs                |   4 
crates/ui2/src/components/icon_button.rs         |   6 
crates/ui2/src/components/input.rs               |   9 
crates/ui2/src/components/keybinding.rs          |   4 
crates/ui2/src/components/label.rs               |   4 
crates/ui2/src/components/modal.rs               |   2 
crates/ui2/src/components/palette.rs             |   8 
crates/ui2/src/components/panel.rs               |   8 
crates/ui2/src/components/stack.rs               |   6 
crates/ui2/src/components/tab.rs                 |   6 
crates/ui2/src/components/toast.rs               |  11 
crates/ui2/src/components/toggle.rs              |   2 
crates/ui2/src/components/tooltip.rs             |   4 
crates/ui2/src/prelude.rs                        |   4 
crates/ui2/src/story.rs                          |   4 
crates/ui2/src/styled_ext.rs                     |  11 
crates/ui2/src/to_extract/assistant_panel.rs     |   6 
crates/ui2/src/to_extract/breadcrumb.rs          |  10 
crates/ui2/src/to_extract/buffer.rs              |   4 
crates/ui2/src/to_extract/buffer_search.rs       |   6 
crates/ui2/src/to_extract/chat_panel.rs          |   9 
crates/ui2/src/to_extract/collab_panel.rs        |   9 
crates/ui2/src/to_extract/command_palette.rs     |   4 
crates/ui2/src/to_extract/copilot.rs             |   4 
crates/ui2/src/to_extract/editor_pane.rs         |   6 
crates/ui2/src/to_extract/language_selector.rs   |   4 
crates/ui2/src/to_extract/multi_buffer.rs        |   4 
crates/ui2/src/to_extract/notifications_panel.rs |  13 
crates/ui2/src/to_extract/panes.rs               |   2 
crates/ui2/src/to_extract/project_panel.rs       |   9 
crates/ui2/src/to_extract/recent_projects.rs     |   4 
crates/ui2/src/to_extract/tab_bar.rs             |   8 
crates/ui2/src/to_extract/terminal.rs            |   4 
crates/ui2/src/to_extract/theme_selector.rs      |   4 
crates/ui2/src/to_extract/title_bar.rs           |  10 
crates/ui2/src/to_extract/toolbar.rs             |   4 
crates/ui2/src/to_extract/traffic_lights.rs      |   4 
crates/ui2/src/to_extract/workspace.rs           |   8 
crates/workspace2/src/dock.rs                    |  12 
crates/workspace2/src/modal_layer.rs             |   6 
crates/workspace2/src/notifications.rs           |   4 
crates/workspace2/src/pane.rs                    |  12 
crates/workspace2/src/status_bar.rs              |   6 
crates/workspace2/src/toolbar.rs                 |   4 
crates/workspace2/src/workspace2.rs              |  30 
82 files changed, 660 insertions(+), 1,906 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -1,9 +1,9 @@
 use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, div, Action, AppContext, Component, Div, EventEmitter, FocusHandle, Keystroke,
-    ParentElement, Render, StatelessInteractive, Styled, View, ViewContext, VisualContext,
-    WeakView, WindowContext,
+    actions, div, prelude::*, Action, AppContext, Component, EventEmitter, FocusHandle, Keystroke,
+    Node, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
+    WindowContext,
 };
 use picker::{Picker, PickerDelegate};
 use std::{
@@ -77,7 +77,7 @@ impl Modal for CommandPalette {
 }
 
 impl Render for CommandPalette {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
         v_stack().w_96().child(self.picker.clone())
@@ -148,7 +148,7 @@ impl CommandPaletteDelegate {
 }
 
 impl PickerDelegate for CommandPaletteDelegate {
-    type ListItem = Div<Picker<Self>>;
+    type ListItem = Node<Picker<Self>>;
 
     fn placeholder_text(&self) -> Arc<str> {
         "Execute a command...".into()

crates/editor2/src/editor.rs 🔗

@@ -39,12 +39,12 @@ use futures::FutureExt;
 use fuzzy::{StringMatch, StringMatchCandidate};
 use git::diff_hunk_to_display;
 use gpui::{
-    action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext,
-    AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
+    action, actions, div, point, prelude::*, px, relative, rems, size, uniform_list, AnyElement,
+    AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
     EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight, HighlightStyle, Hsla,
-    InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render,
-    StatelessInteractive, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View,
-    ViewContext, VisualContext, WeakView, WindowContext,
+    InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
+    Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
+    WeakView, WindowContext,
 };
 use highlight_matching_bracket::refresh_matching_bracket_highlights;
 use hover_popover::{hide_hover, HoverState};

crates/editor2/src/element.rs 🔗

@@ -2442,7 +2442,7 @@ enum Invisible {
 impl Element<Editor> for EditorElement {
     type ElementState = ();
 
-    fn id(&self) -> Option<gpui::ElementId> {
+    fn element_id(&self) -> Option<gpui::ElementId> {
         None
     }
 

crates/editor2/src/items.rs 🔗

@@ -9,7 +9,7 @@ use collections::HashSet;
 use futures::future::try_join_all;
 use gpui::{
     div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter,
-    FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
+    FocusHandle, Model, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
     ViewContext, VisualContext, WeakView,
 };
 use language::{

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -1,7 +1,7 @@
 use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
-    actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
-    StatelessInteractive, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
+    actions, div, prelude::*, AppContext, EventEmitter, Node, ParentComponent, Render,
+    SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
 };
 use text::{Bias, Point};
 use theme::ActiveTheme;
@@ -145,11 +145,11 @@ impl GoToLine {
 }
 
 impl Render for GoToLine {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         modal(cx)
-            .context("GoToLine")
+            .key_context("GoToLine")
             .on_action(Self::cancel)
             .on_action(Self::confirm)
             .w_96()

crates/gpui2/src/element.rs 🔗

@@ -8,7 +8,7 @@ use std::{any::Any, mem};
 pub trait Element<V: 'static> {
     type ElementState: 'static;
 
-    fn id(&self) -> Option<ElementId>;
+    fn element_id(&self) -> Option<ElementId>;
 
     /// Called to initialize this element for the current frame. If this
     /// element had state in a previous frame, it will be passed in for the 3rd argument.
@@ -38,7 +38,7 @@ pub trait Element<V: 'static> {
 #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
 pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
 
-pub trait ParentElement<V: 'static> {
+pub trait ParentComponent<V: 'static> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
 
     fn child(mut self, child: impl Component<V>) -> Self
@@ -120,7 +120,7 @@ where
     E::ElementState: 'static,
 {
     fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
-        let frame_state = if let Some(id) = self.element.id() {
+        let frame_state = if let Some(id) = self.element.element_id() {
             cx.with_element_state(id, |element_state, cx| {
                 let element_state = self.element.initialize(view_state, element_state, cx);
                 ((), element_state)
@@ -142,7 +142,7 @@ where
                 frame_state: initial_frame_state,
             } => {
                 frame_state = initial_frame_state;
-                if let Some(id) = self.element.id() {
+                if let Some(id) = self.element.element_id() {
                     layout_id = cx.with_element_state(id, |element_state, cx| {
                         let mut element_state = element_state.unwrap();
                         let layout_id = self.element.layout(state, &mut element_state, cx);
@@ -181,7 +181,7 @@ where
                 ..
             } => {
                 let bounds = cx.layout_bounds(layout_id);
-                if let Some(id) = self.element.id() {
+                if let Some(id) = self.element.element_id() {
                     cx.with_element_state(id, |element_state, cx| {
                         let mut element_state = element_state.unwrap();
                         self.element
@@ -351,7 +351,7 @@ where
 {
     type ElementState = AnyElement<V>;
 
-    fn id(&self) -> Option<ElementId> {
+    fn element_id(&self) -> Option<ElementId> {
         None
     }
 

crates/gpui2/src/elements.rs 🔗

@@ -1,12 +1,13 @@
-mod div;
+// mod div;
 mod img;
 mod node;
 mod svg;
 mod text;
 mod uniform_list;
 
-pub use div::*;
+// pub use div::*;
 pub use img::*;
+pub use node::*;
 pub use svg::*;
 pub use text::*;
 pub use uniform_list::*;

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

@@ -55,16 +55,6 @@ where
     I: ElementInteractivity<V>,
     F: KeyDispatch<V>,
 {
-    pub fn group(mut self, group: impl Into<SharedString>) -> Self {
-        self.group = Some(group.into());
-        self
-    }
-
-    pub fn z_index(mut self, z_index: u32) -> Self {
-        self.base_style.z_index = Some(z_index);
-        self
-    }
-
     pub fn context<C>(mut self, context: C) -> Self
     where
         Self: Sized,
@@ -77,22 +67,6 @@ where
         self
     }
 
-    pub fn overflow_hidden(mut self) -> Self {
-        self.base_style.overflow.x = Some(Overflow::Hidden);
-        self.base_style.overflow.y = Some(Overflow::Hidden);
-        self
-    }
-
-    pub fn overflow_hidden_x(mut self) -> Self {
-        self.base_style.overflow.x = Some(Overflow::Hidden);
-        self
-    }
-
-    pub fn overflow_hidden_y(mut self) -> Self {
-        self.base_style.overflow.y = Some(Overflow::Hidden);
-        self
-    }
-
     pub fn compute_style(
         &self,
         bounds: Bounds<Pixels>,
@@ -135,22 +109,6 @@ impl<V: 'static> Div<V, StatefulInteractivity<V>, NonFocusableKeyDispatch> {
             base_style: self.base_style,
         }
     }
-
-    pub fn overflow_scroll(mut self) -> Self {
-        self.base_style.overflow.x = Some(Overflow::Scroll);
-        self.base_style.overflow.y = Some(Overflow::Scroll);
-        self
-    }
-
-    pub fn overflow_x_scroll(mut self) -> Self {
-        self.base_style.overflow.x = Some(Overflow::Scroll);
-        self
-    }
-
-    pub fn overflow_y_scroll(mut self) -> Self {
-        self.base_style.overflow.y = Some(Overflow::Scroll);
-        self
-    }
 }
 
 impl<V: 'static> Div<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {

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

@@ -1,35 +1,28 @@
 use crate::{
-    div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementId,
-    ElementInteractivity, FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
-    NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
-    StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
+    AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
+    InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
+    Styled, ViewContext,
 };
 use futures::FutureExt;
 use util::ResultExt;
 
-pub struct Img<
-    V: 'static,
-    I: ElementInteractivity<V> = StatelessInteractivity<V>,
-    F: KeyDispatch<V> = NonFocusableKeyDispatch,
-> {
-    base: Div<V, I, F>,
+pub struct Img<V: 'static> {
+    interactivity: Interactivity<V>,
     uri: Option<SharedString>,
     grayscale: bool,
 }
 
-pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
+pub fn img<V: 'static>() -> Img<V> {
     Img {
-        base: div(),
+        interactivity: Interactivity::default(),
         uri: None,
         grayscale: false,
     }
 }
 
-impl<V, I, F> Img<V, I, F>
+impl<V> Img<V>
 where
     V: 'static,
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
 {
     pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
         self.uri = Some(uri.into());
@@ -42,38 +35,17 @@ where
     }
 }
 
-impl<V, F> Img<V, StatelessInteractivity<V>, F>
-where
-    F: KeyDispatch<V>,
-{
-    pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
-        Img {
-            base: self.base.id(id),
-            uri: self.uri,
-            grayscale: self.grayscale,
-        }
-    }
-}
-
-impl<V, I, F> Component<V> for Img<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
+impl<V> Component<V> for Img<V> {
     fn render(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<V, I, F> Element<V> for Img<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
-    type ElementState = DivState;
+impl<V> Element<V> for Img<V> {
+    type ElementState = InteractiveElementState;
 
-    fn id(&self) -> Option<crate::ElementId> {
-        self.base.id()
+    fn element_id(&self) -> Option<crate::ElementId> {
+        self.interactivity.element_id.clone()
     }
 
     fn initialize(
@@ -82,7 +54,7 @@ where
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<V>,
     ) -> Self::ElementState {
-        self.base.initialize(view_state, element_state, cx)
+        self.interactivity.initialize(element_state, cx)
     }
 
     fn layout(
@@ -91,7 +63,9 @@ where
         element_state: &mut Self::ElementState,
         cx: &mut ViewContext<V>,
     ) -> LayoutId {
-        self.base.layout(view_state, element_state, cx)
+        self.interactivity.layout(element_state, cx, |style, cx| {
+            cx.request_layout(&style, None)
+        })
     }
 
     fn paint(
@@ -101,86 +75,50 @@ where
         element_state: &mut Self::ElementState,
         cx: &mut ViewContext<V>,
     ) {
-        cx.with_z_index(0, |cx| {
-            self.base.paint(bounds, view, element_state, cx);
-        });
-
-        let style = self.base.compute_style(bounds, element_state, cx);
-        let corner_radii = style.corner_radii;
-
-        if let Some(uri) = self.uri.clone() {
-            // eprintln!(">>> image_cache.get({uri}");
-            let image_future = cx.image_cache.get(uri.clone());
-            // eprintln!("<<< image_cache.get({uri}");
-            if let Some(data) = image_future
-                .clone()
-                .now_or_never()
-                .and_then(ResultExt::log_err)
-            {
-                let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
-                cx.with_z_index(1, |cx| {
-                    cx.paint_image(bounds, corner_radii, data, self.grayscale)
-                        .log_err()
-                });
-            } else {
-                cx.spawn(|_, mut cx| async move {
-                    if image_future.await.log_err().is_some() {
-                        cx.on_next_frame(|cx| cx.notify());
+        self.interactivity.paint(
+            bounds,
+            bounds.size,
+            element_state,
+            cx,
+            |style, scroll_offset, cx| {
+                let corner_radii = style.corner_radii;
+
+                if let Some(uri) = self.uri.clone() {
+                    // eprintln!(">>> image_cache.get({uri}");
+                    let image_future = cx.image_cache.get(uri.clone());
+                    // eprintln!("<<< image_cache.get({uri}");
+                    if let Some(data) = image_future
+                        .clone()
+                        .now_or_never()
+                        .and_then(ResultExt::log_err)
+                    {
+                        let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
+                        cx.with_z_index(1, |cx| {
+                            cx.paint_image(bounds, corner_radii, data, self.grayscale)
+                                .log_err()
+                        });
+                    } else {
+                        cx.spawn(|_, mut cx| async move {
+                            if image_future.await.log_err().is_some() {
+                                cx.on_next_frame(|cx| cx.notify());
+                            }
+                        })
+                        .detach()
                     }
-                })
-                .detach()
-            }
-        }
+                }
+            },
+        )
     }
 }
 
-impl<V, I, F> Styled for Img<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
+impl<V> Styled for Img<V> {
     fn style(&mut self) -> &mut StyleRefinement {
-        self.base.style()
+        &mut self.interactivity.base_style
     }
 }
 
-impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
-    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
-        self.base.stateless_interactivity()
-    }
-}
-
-impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
-where
-    F: KeyDispatch<V>,
-{
-    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
-        self.base.stateful_interactivity()
-    }
-}
-
-impl<V, I> Focusable<V> for Img<V, I, FocusableKeyDispatch<V>>
-where
-    V: 'static,
-    I: ElementInteractivity<V>,
-{
-    fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
-        self.base.focus_listeners()
-    }
-
-    fn set_focus_style(&mut self, style: StyleRefinement) {
-        self.base.set_focus_style(style)
-    }
-
-    fn set_focus_in_style(&mut self, style: StyleRefinement) {
-        self.base.set_focus_in_style(style)
-    }
-
-    fn set_in_focus_style(&mut self, style: StyleRefinement) {
-        self.base.set_in_focus_style(style)
+impl<V> InteractiveComponent<V> for Img<V> {
+    fn interactivity(&mut self) -> &mut Interactivity<V> {
+        &mut self.interactivity
     }
 }

crates/gpui2/src/elements/node.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{
     point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
-    BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
-    KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
-    MouseUpEvent, Pixels, Point, Render, ScrollWheelEvent, SharedString, Size, Style,
-    StyleRefinement, Styled, Task, View, ViewContext, Visibility,
+    BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
+    FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
+    MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
+    SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
@@ -32,6 +32,11 @@ pub struct GroupStyle {
 pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
     fn interactivity(&mut self) -> &mut Interactivity<V>;
 
+    fn group(mut self, group: impl Into<SharedString>) -> Self {
+        self.interactivity().group = Some(group.into());
+        self
+    }
+
     fn id(mut self, id: impl Into<ElementId>) -> Stateful<V, Self> {
         self.interactivity().element_id = Some(id.into());
 
@@ -41,9 +46,9 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
         }
     }
 
-    fn track_focus(mut self, focus_handle: FocusHandle) -> Focusable<V, Self> {
+    fn track_focus(mut self, focus_handle: &FocusHandle) -> Focusable<V, Self> {
         self.interactivity().focusable = true;
-        self.interactivity().tracked_focus_handle = Some(focus_handle);
+        self.interactivity().tracked_focus_handle = Some(focus_handle.clone());
         Focusable {
             element: self,
             view_type: PhantomData,
@@ -269,8 +274,27 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
 }
 
 pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
-    fn focusable(mut self) -> Self {
+    fn focusable(mut self) -> Focusable<V, Self> {
         self.interactivity().focusable = true;
+        Focusable {
+            element: self,
+            view_type: PhantomData,
+        }
+    }
+
+    fn overflow_scroll(mut self) -> Self {
+        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
+        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
+        self
+    }
+
+    fn overflow_x_scroll(mut self) -> Self {
+        self.interactivity().base_style.overflow.x = Some(Overflow::Scroll);
+        self
+    }
+
+    fn overflow_y_scroll(mut self) -> Self {
+        self.interactivity().base_style.overflow.y = Some(Overflow::Scroll);
         self
     }
 
@@ -514,16 +538,16 @@ pub type KeyUpListener<V> =
 pub type ActionListener<V> =
     Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
 
-pub fn node<V: 'static>() -> Node<V> {
+pub fn div<V: 'static>() -> Node<V> {
     Node {
         interactivity: Interactivity::default(),
-        children: Vec::default(),
+        children: SmallVec::default(),
     }
 }
 
 pub struct Node<V> {
     interactivity: Interactivity<V>,
-    children: Vec<AnyElement<V>>,
+    children: SmallVec<[AnyElement<V>; 2]>,
 }
 
 impl<V> Styled for Node<V> {
@@ -538,10 +562,16 @@ impl<V: 'static> InteractiveComponent<V> for Node<V> {
     }
 }
 
+impl<V: 'static> ParentComponent<V> for Node<V> {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        &mut self.children
+    }
+}
+
 impl<V: 'static> Element<V> for Node<V> {
     type ElementState = NodeState;
 
-    fn id(&self) -> Option<ElementId> {
+    fn element_id(&self) -> Option<ElementId> {
         self.interactivity.element_id.clone()
     }
 
@@ -641,48 +671,54 @@ impl<V: 'static> Element<V> for Node<V> {
     }
 }
 
+impl<V: 'static> Component<V> for Node<V> {
+    fn render(self) -> AnyElement<V> {
+        AnyElement::new(self)
+    }
+}
+
 pub struct NodeState {
     child_layout_ids: SmallVec<[LayoutId; 4]>,
     interactive_state: InteractiveElementState,
 }
 
 pub struct Interactivity<V> {
-    element_id: Option<ElementId>,
-    key_context: KeyContext,
-    focusable: bool,
-    tracked_focus_handle: Option<FocusHandle>,
-    focus_listeners: FocusListeners<V>,
-    scroll_offset: Point<Pixels>,
-    group: Option<SharedString>,
-    base_style: StyleRefinement,
-    focus_style: StyleRefinement,
-    focus_in_style: StyleRefinement,
-    in_focus_style: StyleRefinement,
-    hover_style: StyleRefinement,
-    group_hover_style: Option<GroupStyle>,
-    active_style: StyleRefinement,
-    group_active_style: Option<GroupStyle>,
-    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
-    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
-    mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
-    mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
-    mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
-    scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
-    key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
-    key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
-    action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
-    drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
-    click_listeners: SmallVec<[ClickListener<V>; 2]>,
-    drag_listener: Option<DragListener<V>>,
-    hover_listener: Option<HoverListener<V>>,
-    tooltip_builder: Option<TooltipBuilder<V>>,
+    pub element_id: Option<ElementId>,
+    pub key_context: KeyContext,
+    pub focusable: bool,
+    pub tracked_focus_handle: Option<FocusHandle>,
+    pub focus_listeners: FocusListeners<V>,
+    // pub scroll_offset: Point<Pixels>,
+    pub group: Option<SharedString>,
+    pub base_style: StyleRefinement,
+    pub focus_style: StyleRefinement,
+    pub focus_in_style: StyleRefinement,
+    pub in_focus_style: StyleRefinement,
+    pub hover_style: StyleRefinement,
+    pub group_hover_style: Option<GroupStyle>,
+    pub active_style: StyleRefinement,
+    pub group_active_style: Option<GroupStyle>,
+    pub drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
+    pub group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
+    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_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
+    pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
+    pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
+    pub drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
+    pub click_listeners: SmallVec<[ClickListener<V>; 2]>,
+    pub drag_listener: Option<DragListener<V>>,
+    pub hover_listener: Option<HoverListener<V>>,
+    pub tooltip_builder: Option<TooltipBuilder<V>>,
 }
 
 impl<V> Interactivity<V>
 where
     V: 'static,
 {
-    fn initialize(
+    pub fn initialize(
         &mut self,
         element_state: Option<InteractiveElementState>,
         cx: &mut ViewContext<V>,
@@ -703,7 +739,7 @@ where
         element_state
     }
 
-    fn layout(
+    pub fn layout(
         &mut self,
         element_state: &mut InteractiveElementState,
         cx: &mut ViewContext<V>,
@@ -719,7 +755,7 @@ where
         })
     }
 
-    fn paint(
+    pub fn paint(
         &mut self,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
@@ -996,6 +1032,11 @@ where
             GroupBounds::push(group, bounds, cx);
         }
 
+        let scroll_offset = element_state
+            .scroll_offset
+            .as_ref()
+            .map(|scroll_offset| *scroll_offset.lock());
+
         cx.with_element_id(self.element_id.clone(), |cx| {
             cx.with_key_dispatch(
                 self.key_context.clone(),
@@ -1026,7 +1067,7 @@ where
                         }
                     }
 
-                    f(style, self.scroll_offset, cx)
+                    f(style, scroll_offset.unwrap_or_default(), cx)
                 },
             );
         });
@@ -1036,7 +1077,7 @@ where
         }
     }
 
-    fn compute_style(
+    pub fn compute_style(
         &self,
         bounds: Option<Bounds<Pixels>>,
         element_state: &mut InteractiveElementState,
@@ -1118,7 +1159,7 @@ impl<V: 'static> Default for Interactivity<V> {
             focusable: false,
             tracked_focus_handle: None,
             focus_listeners: SmallVec::default(),
-            scroll_offset: Point::default(),
+            // scroll_offset: Point::default(),
             group: None,
             base_style: StyleRefinement::default(),
             focus_style: StyleRefinement::default(),
@@ -1148,15 +1189,15 @@ impl<V: 'static> Default for Interactivity<V> {
 
 #[derive(Default)]
 pub struct InteractiveElementState {
-    focus_handle: Option<FocusHandle>,
-    clicked_state: Arc<Mutex<ElementClickedState>>,
-    hover_state: Arc<Mutex<bool>>,
-    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
-    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
-    active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
+    pub focus_handle: Option<FocusHandle>,
+    pub clicked_state: Arc<Mutex<ElementClickedState>>,
+    pub hover_state: Arc<Mutex<bool>>,
+    pub pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
+    pub scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
+    pub active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
 }
 
-struct ActiveTooltip {
+pub struct ActiveTooltip {
     #[allow(unused)] // used to drop the task
     waiting: Option<Task<()>>,
     tooltip: Option<AnyTooltip>,
@@ -1164,7 +1205,7 @@ struct ActiveTooltip {
 
 /// Whether or not the element or a group that contains it is clicked by the mouse.
 #[derive(Copy, Clone, Default, Eq, PartialEq)]
-struct ElementClickedState {
+pub struct ElementClickedState {
     pub group: bool,
     pub element: bool,
 }
@@ -1222,6 +1263,16 @@ impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveCompo
 {
 }
 
+impl<V, E> Styled for Focusable<V, E>
+where
+    V: 'static,
+    E: Styled,
+{
+    fn style(&mut self) -> &mut StyleRefinement {
+        self.element.style()
+    }
+}
+
 impl<V, E> Element<V> for Focusable<V, E>
 where
     V: 'static,
@@ -1229,8 +1280,8 @@ where
 {
     type ElementState = E::ElementState;
 
-    fn id(&self) -> Option<ElementId> {
-        self.element.id()
+    fn element_id(&self) -> Option<ElementId> {
+        self.element.element_id()
     }
 
     fn initialize(
@@ -1262,11 +1313,41 @@ where
     }
 }
 
+impl<V, E> Component<V> for Focusable<V, E>
+where
+    V: 'static,
+    E: 'static + Element<V>,
+{
+    fn render(self) -> AnyElement<V> {
+        AnyElement::new(self)
+    }
+}
+
+impl<V, E> ParentComponent<V> for Focusable<V, E>
+where
+    V: 'static,
+    E: ParentComponent<V>,
+{
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        self.element.children_mut()
+    }
+}
+
 pub struct Stateful<V, E> {
     element: E,
     view_type: PhantomData<V>,
 }
 
+impl<V, E> Styled for Stateful<V, E>
+where
+    V: 'static,
+    E: Styled,
+{
+    fn style(&mut self) -> &mut StyleRefinement {
+        self.element.style()
+    }
+}
+
 impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
 where
     V: 'static,
@@ -1294,8 +1375,8 @@ where
 {
     type ElementState = E::ElementState;
 
-    fn id(&self) -> Option<ElementId> {
-        self.element.id()
+    fn element_id(&self) -> Option<ElementId> {
+        self.element.element_id()
     }
 
     fn initialize(
@@ -1326,3 +1407,23 @@ where
         self.element.paint(bounds, view_state, element_state, cx)
     }
 }
+
+impl<V, E> Component<V> for Stateful<V, E>
+where
+    V: 'static,
+    E: 'static + Element<V>,
+{
+    fn render(self) -> AnyElement<V> {
+        AnyElement::new(self)
+    }
+}
+
+impl<V, E> ParentComponent<V> for Stateful<V, E>
+where
+    V: 'static,
+    E: ParentComponent<V>,
+{
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
+        self.element.children_mut()
+    }
+}

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

@@ -1,69 +1,40 @@
 use crate::{
-    div, AnyElement, Bounds, Component, Div, DivState, Element, ElementId, ElementInteractivity,
-    FocusListeners, Focusable, FocusableKeyDispatch, KeyDispatch, LayoutId,
-    NonFocusableKeyDispatch, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
-    StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
+    AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
+    InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
+    Styled, ViewContext,
 };
 use util::ResultExt;
 
-pub struct Svg<
-    V: 'static,
-    I: ElementInteractivity<V> = StatelessInteractivity<V>,
-    F: KeyDispatch<V> = NonFocusableKeyDispatch,
-> {
-    base: Div<V, I, F>,
+pub struct Svg<V: 'static> {
+    interactivity: Interactivity<V>,
     path: Option<SharedString>,
 }
 
-pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, NonFocusableKeyDispatch> {
+pub fn svg<V: 'static>() -> Svg<V> {
     Svg {
-        base: div(),
+        interactivity: Interactivity::default(),
         path: None,
     }
 }
 
-impl<V, I, F> Svg<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
+impl<V> Svg<V> {
     pub fn path(mut self, path: impl Into<SharedString>) -> Self {
         self.path = Some(path.into());
         self
     }
 }
 
-impl<V, F> Svg<V, StatelessInteractivity<V>, F>
-where
-    F: KeyDispatch<V>,
-{
-    pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
-        Svg {
-            base: self.base.id(id),
-            path: self.path,
-        }
-    }
-}
-
-impl<V, I, F> Component<V> for Svg<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
+impl<V> Component<V> for Svg<V> {
     fn render(self) -> AnyElement<V> {
         AnyElement::new(self)
     }
 }
 
-impl<V, I, F> Element<V> for Svg<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
-    type ElementState = DivState;
+impl<V> Element<V> for Svg<V> {
+    type ElementState = InteractiveElementState;
 
-    fn id(&self) -> Option<crate::ElementId> {
-        self.base.id()
+    fn element_id(&self) -> Option<ElementId> {
+        self.interactivity.element_id.clone()
     }
 
     fn initialize(
@@ -72,7 +43,7 @@ where
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<V>,
     ) -> Self::ElementState {
-        self.base.initialize(view_state, element_state, cx)
+        self.interactivity.initialize(element_state, cx)
     }
 
     fn layout(
@@ -81,7 +52,9 @@ where
         element_state: &mut Self::ElementState,
         cx: &mut ViewContext<V>,
     ) -> LayoutId {
-        self.base.layout(view_state, element_state, cx)
+        self.interactivity.layout(element_state, cx, |style, cx| {
+            cx.request_layout(&style, None)
+        })
     }
 
     fn paint(
@@ -93,65 +66,23 @@ where
     ) where
         Self: Sized,
     {
-        self.base.paint(bounds, view, element_state, cx);
-        let color = self
-            .base
-            .compute_style(bounds, element_state, cx)
-            .text
-            .color;
-        if let Some((path, color)) = self.path.as_ref().zip(color) {
-            cx.paint_svg(bounds, path.clone(), color).log_err();
-        }
+        self.interactivity
+            .paint(bounds, bounds.size, element_state, cx, |style, _, cx| {
+                if let Some((path, color)) = self.path.as_ref().zip(style.text.color) {
+                    cx.paint_svg(bounds, path.clone(), color).log_err();
+                }
+            })
     }
 }
 
-impl<V, I, F> Styled for Svg<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
+impl<V> Styled for Svg<V> {
     fn style(&mut self) -> &mut StyleRefinement {
-        self.base.style()
-    }
-}
-
-impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
-    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
-        self.base.stateless_interactivity()
+        &mut self.interactivity.base_style
     }
 }
 
-impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
-where
-    V: 'static,
-    F: KeyDispatch<V>,
-{
-    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
-        self.base.stateful_interactivity()
-    }
-}
-
-impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusableKeyDispatch<V>>
-where
-    I: ElementInteractivity<V>,
-{
-    fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
-        self.base.focus_listeners()
-    }
-
-    fn set_focus_style(&mut self, style: StyleRefinement) {
-        self.base.set_focus_style(style)
-    }
-
-    fn set_focus_in_style(&mut self, style: StyleRefinement) {
-        self.base.set_focus_in_style(style)
-    }
-
-    fn set_in_focus_style(&mut self, style: StyleRefinement) {
-        self.base.set_in_focus_style(style)
+impl<V> InteractiveComponent<V> for Svg<V> {
+    fn interactivity(&mut self) -> &mut Interactivity<V> {
+        &mut self.interactivity
     }
 }

crates/gpui2/src/elements/text.rs 🔗

@@ -53,7 +53,7 @@ impl<V: 'static> Component<V> for Text<V> {
 impl<V: 'static> Element<V> for Text<V> {
     type ElementState = Arc<Mutex<Option<TextElementState>>>;
 
-    fn id(&self) -> Option<crate::ElementId> {
+    fn element_id(&self) -> Option<crate::ElementId> {
         None
     }
 

crates/gpui2/src/elements/uniform_list.rs 🔗

@@ -1,24 +1,23 @@
 use crate::{
     point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
-    ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
-    StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
-    StyleRefinement, Styled, ViewContext,
+    ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
+    Point, Size, StyleRefinement, Styled, ViewContext,
 };
 use parking_lot::Mutex;
 use smallvec::SmallVec;
-use std::{cmp, ops::Range, sync::Arc};
+use std::{cmp, mem, ops::Range, sync::Arc};
 use taffy::style::Overflow;
 
 /// uniform_list provides lazy rendering for a set of items that are of uniform height.
 /// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
 /// uniform_list will only render the visibile subset of items.
-pub fn uniform_list<Id, V, C>(
-    id: Id,
+pub fn uniform_list<I, V, C>(
+    id: I,
     item_count: usize,
     f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
 ) -> UniformList<V>
 where
-    Id: Into<ElementId>,
+    I: Into<ElementId>,
     V: 'static,
     C: Component<V>,
 {
@@ -37,7 +36,10 @@ where
                 .map(|component| component.render())
                 .collect()
         }),
-        interactivity: StatefulInteractivity::new(id, StatelessInteractivity::default()),
+        interactivity: Interactivity {
+            element_id: Some(id.into()),
+            ..Default::default()
+        },
         scroll_handle: None,
     }
 }
@@ -54,7 +56,7 @@ pub struct UniformList<V: 'static> {
             &'a mut ViewContext<V>,
         ) -> SmallVec<[AnyElement<V>; 64]>,
     >,
-    interactivity: StatefulInteractivity<V>,
+    interactivity: Interactivity<V>,
     scroll_handle: Option<UniformListScrollHandle>,
 }
 
@@ -103,7 +105,7 @@ pub struct UniformListState {
 impl<V: 'static> Element<V> for UniformList<V> {
     type ElementState = UniformListState;
 
-    fn id(&self) -> Option<crate::ElementId> {
+    fn element_id(&self) -> Option<crate::ElementId> {
         Some(self.id.clone())
     }
 
@@ -113,13 +115,18 @@ impl<V: 'static> Element<V> for UniformList<V> {
         element_state: Option<Self::ElementState>,
         cx: &mut ViewContext<V>,
     ) -> Self::ElementState {
-        element_state.unwrap_or_else(|| {
+        if let Some(mut element_state) = element_state {
+            element_state.interactive = self
+                .interactivity
+                .initialize(Some(element_state.interactive), cx);
+            element_state
+        } else {
             let item_size = self.measure_item(view_state, None, cx);
             UniformListState {
-                interactive: InteractiveElementState::default(),
+                interactive: self.interactivity.initialize(None, cx),
                 item_size,
             }
-        })
+        }
     }
 
     fn layout(
@@ -132,35 +139,44 @@ impl<V: 'static> Element<V> for UniformList<V> {
         let item_size = element_state.item_size;
         let rem_size = cx.rem_size();
 
-        cx.request_measured_layout(
-            self.computed_style(),
-            rem_size,
-            move |known_dimensions: Size<Option<Pixels>>, available_space: Size<AvailableSpace>| {
-                let desired_height = item_size.height * max_items;
-                let width = known_dimensions
-                    .width
-                    .unwrap_or(match available_space.width {
-                        AvailableSpace::Definite(x) => x,
-                        AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
-                    });
-                let height = match available_space.height {
-                    AvailableSpace::Definite(x) => desired_height.min(x),
-                    AvailableSpace::MinContent | AvailableSpace::MaxContent => desired_height,
-                };
-                size(width, height)
-            },
-        )
+        self.interactivity
+            .layout(&mut element_state.interactive, cx, |style, cx| {
+                cx.request_measured_layout(
+                    style,
+                    rem_size,
+                    move |known_dimensions: Size<Option<Pixels>>,
+                          available_space: Size<AvailableSpace>| {
+                        let desired_height = item_size.height * max_items;
+                        let width = known_dimensions
+                            .width
+                            .unwrap_or(match available_space.width {
+                                AvailableSpace::Definite(x) => x,
+                                AvailableSpace::MinContent | AvailableSpace::MaxContent => {
+                                    item_size.width
+                                }
+                            });
+                        let height = match available_space.height {
+                            AvailableSpace::Definite(x) => desired_height.min(x),
+                            AvailableSpace::MinContent | AvailableSpace::MaxContent => {
+                                desired_height
+                            }
+                        };
+                        size(width, height)
+                    },
+                )
+            })
     }
 
     fn paint(
         &mut self,
-        bounds: crate::Bounds<crate::Pixels>,
+        bounds: Bounds<crate::Pixels>,
         view_state: &mut V,
         element_state: &mut Self::ElementState,
         cx: &mut ViewContext<V>,
     ) {
-        let style = self.computed_style();
-
+        let style =
+            self.interactivity
+                .compute_style(Some(bounds), &mut element_state.interactive, cx);
         let border = style.border_widths.to_pixels(cx.rem_size());
         let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
 
@@ -170,74 +186,75 @@ impl<V: 'static> Element<V> for UniformList<V> {
                 - point(border.right + padding.right, border.bottom + padding.bottom),
         );
 
-        cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
-            style.paint(bounds, cx);
+        let item_size = element_state.item_size;
+        let content_size = Size {
+            width: padded_bounds.size.width,
+            height: item_size.height * self.item_count,
+        };
 
-            let content_size;
-            if self.item_count > 0 {
-                let item_height = self
-                    .measure_item(view_state, Some(padded_bounds.size.width), cx)
-                    .height;
-                if let Some(scroll_handle) = self.scroll_handle.clone() {
-                    scroll_handle.0.lock().replace(ScrollHandleState {
-                        item_height,
-                        list_height: padded_bounds.size.height,
-                        scroll_offset: element_state.interactive.track_scroll_offset(),
-                    });
-                }
-                let visible_item_count = if item_height > px(0.) {
-                    (padded_bounds.size.height / item_height).ceil() as usize + 1
-                } else {
-                    0
-                };
-                let scroll_offset = element_state
-                    .interactive
-                    .scroll_offset()
-                    .map_or((0.0).into(), |offset| offset.y);
-                let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
-                let visible_range = first_visible_element_ix
-                    ..cmp::min(
-                        first_visible_element_ix + visible_item_count,
-                        self.item_count,
-                    );
+        let mut interactivity = mem::take(&mut self.interactivity);
+        let shared_scroll_offset = element_state.interactive.scroll_offset.clone().unwrap();
 
-                let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
+        interactivity.paint(
+            bounds,
+            content_size,
+            &mut element_state.interactive,
+            cx,
+            |style, scroll_offset, cx| {
+                let border = style.border_widths.to_pixels(cx.rem_size());
+                let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
 
-                content_size = Size {
-                    width: padded_bounds.size.width,
-                    height: item_height * self.item_count,
-                };
+                let padded_bounds = Bounds::from_corners(
+                    bounds.origin + point(border.left + padding.left, border.top + padding.top),
+                    bounds.lower_right()
+                        - point(border.right + padding.right, border.bottom + padding.bottom),
+                );
 
-                cx.with_z_index(1, |cx| {
-                    for (item, ix) in items.iter_mut().zip(visible_range) {
-                        let item_origin =
-                            padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
-                        let available_space = size(
-                            AvailableSpace::Definite(padded_bounds.size.width),
-                            AvailableSpace::Definite(item_height),
-                        );
-                        item.draw(item_origin, available_space, view_state, cx);
-                    }
-                });
-            } else {
-                content_size = Size {
-                    width: bounds.size.width,
-                    height: px(0.),
-                };
-            }
+                cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
+                    style.paint(bounds, cx);
 
-            let overflow = point(style.overflow.x, Overflow::Scroll);
+                    if self.item_count > 0 {
+                        let item_height = self
+                            .measure_item(view_state, Some(padded_bounds.size.width), cx)
+                            .height;
+                        if let Some(scroll_handle) = self.scroll_handle.clone() {
+                            scroll_handle.0.lock().replace(ScrollHandleState {
+                                item_height,
+                                list_height: padded_bounds.size.height,
+                                scroll_offset: shared_scroll_offset,
+                            });
+                        }
+                        let visible_item_count = if item_height > px(0.) {
+                            (padded_bounds.size.height / item_height).ceil() as usize + 1
+                        } else {
+                            0
+                        };
 
-            cx.with_z_index(0, |cx| {
-                self.interactivity.handle_events(
-                    bounds,
-                    content_size,
-                    overflow,
-                    &mut element_state.interactive,
-                    cx,
-                );
-            });
-        })
+                        let first_visible_element_ix =
+                            (-scroll_offset.y / item_height).floor() as usize;
+                        let visible_range = first_visible_element_ix
+                            ..cmp::min(
+                                first_visible_element_ix + visible_item_count,
+                                self.item_count,
+                            );
+
+                        let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
+                        cx.with_z_index(1, |cx| {
+                            for (item, ix) in items.iter_mut().zip(visible_range) {
+                                let item_origin = padded_bounds.origin
+                                    + point(px(0.), item_height * ix + scroll_offset.y);
+                                let available_space = size(
+                                    AvailableSpace::Definite(padded_bounds.size.width),
+                                    AvailableSpace::Definite(item_height),
+                                );
+                                item.draw(item_origin, available_space, view_state, cx);
+                            }
+                        });
+                    }
+                })
+            },
+        );
+        self.interactivity = interactivity;
     }
 }
 
@@ -275,14 +292,8 @@ impl<V> UniformList<V> {
     }
 }
 
-impl<V: 'static> StatelessInteractive<V> for UniformList<V> {
-    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
-        self.interactivity.as_stateless_mut()
-    }
-}
-
-impl<V: 'static> StatefulInteractive<V> for UniformList<V> {
-    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
+impl<V> InteractiveComponent<V> for UniformList<V> {
+    fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
         &mut self.interactivity
     }
 }

crates/gpui2/src/interactive.rs 🔗

@@ -1,915 +1,17 @@
 use crate::{
-    div, point, px, Action, AnyDrag, AnyTooltip, AnyView, AppContext, Bounds, Component,
-    DispatchPhase, Div, Element, ElementId, FocusHandle, KeyContext, Keystroke, Modifiers,
-    Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, Task, View,
-    ViewContext,
+    div, point, px, AnyDrag, AnyTooltip, AnyView, AppContext, Bounds, Component, DispatchPhase,
+    FocusHandle, Keystroke, Modifiers, Node, Pixels, Point, Render, SharedString, StyleRefinement,
+    Task, ViewContext,
 };
-use collections::HashMap;
-use derive_more::{Deref, DerefMut};
-use parking_lot::Mutex;
-use refineable::Refineable;
 use smallvec::SmallVec;
 use std::{
-    any::{Any, TypeId},
-    fmt::Debug,
-    marker::PhantomData,
-    mem,
-    ops::Deref,
-    path::PathBuf,
-    sync::Arc,
-    time::Duration,
+    any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf, sync::Arc, time::Duration,
 };
 
 const DRAG_THRESHOLD: f64 = 2.;
 const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
 const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
 
-pub trait StatelessInteractive<V: 'static>: Element<V> {
-    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V>;
-
-    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().hover_style = f(StyleRefinement::default());
-        self
-    }
-
-    fn group_hover(
-        mut self,
-        group_name: impl Into<SharedString>,
-        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().group_hover_style = Some(GroupStyle {
-            group: group_name.into(),
-            style: f(StyleRefinement::default()),
-        });
-        self
-    }
-
-    fn on_mouse_down(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .mouse_down_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && event.button == button
-                    && bounds.contains_point(&event.position)
-                {
-                    handler(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_mouse_up(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .mouse_up_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble
-                    && event.button == button
-                    && bounds.contains_point(&event.position)
-                {
-                    handler(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_mouse_down_out(
-        mut self,
-        handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .mouse_down_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Capture && !bounds.contains_point(&event.position) {
-                    handler(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_mouse_up_out(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .mouse_up_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Capture
-                    && event.button == button
-                    && !bounds.contains_point(&event.position)
-                {
-                    handler(view, event, cx);
-                }
-            }));
-        self
-    }
-
-    fn on_mouse_move(
-        mut self,
-        handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .mouse_move_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    handler(view, event, cx);
-                }
-            }));
-        self
-    }
-
-    fn on_scroll_wheel(
-        mut self,
-        handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .scroll_wheel_listeners
-            .push(Box::new(move |view, event, bounds, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    handler(view, event, cx);
-                }
-            }));
-        self
-    }
-
-    /// Capture the given action, fires during the capture phase
-    fn capture_action<A: Action>(
-        mut self,
-        listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().action_listeners.push((
-            TypeId::of::<A>(),
-            Box::new(move |view, action, phase, cx| {
-                let action = action.downcast_ref().unwrap();
-                if phase == DispatchPhase::Capture {
-                    listener(view, action, cx)
-                }
-            }),
-        ));
-        self
-    }
-
-    /// Add a listener for the given action, fires during the bubble event phase
-    fn on_action<A: Action>(
-        mut self,
-        listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().action_listeners.push((
-            TypeId::of::<A>(),
-            Box::new(move |view, action, phase, cx| {
-                let action = action.downcast_ref().unwrap();
-                if phase == DispatchPhase::Bubble {
-                    listener(view, action, cx)
-                }
-            }),
-        ));
-        self
-    }
-
-    fn on_key_down(
-        mut self,
-        listener: impl Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .key_down_listeners
-            .push(Box::new(move |view, event, phase, cx| {
-                listener(view, event, phase, cx)
-            }));
-        self
-    }
-
-    fn on_key_up(
-        mut self,
-        listener: impl Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .key_up_listeners
-            .push(Box::new(move |view, event, phase, cx| {
-                listener(view, event, phase, cx)
-            }));
-        self
-    }
-
-    fn drag_over<S: 'static>(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity()
-            .drag_over_styles
-            .push((TypeId::of::<S>(), f(StyleRefinement::default())));
-        self
-    }
-
-    fn group_drag_over<S: 'static>(
-        mut self,
-        group_name: impl Into<SharedString>,
-        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().group_drag_over_styles.push((
-            TypeId::of::<S>(),
-            GroupStyle {
-                group: group_name.into(),
-                style: f(StyleRefinement::default()),
-            },
-        ));
-        self
-    }
-
-    fn on_drop<W: 'static>(
-        mut self,
-        listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateless_interactivity().drop_listeners.push((
-            TypeId::of::<W>(),
-            Box::new(move |view, dragged_view, cx| {
-                listener(view, dragged_view.downcast().unwrap(), cx);
-            }),
-        ));
-        self
-    }
-}
-
-pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
-    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V>;
-
-    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateful_interactivity().active_style = f(StyleRefinement::default());
-        self
-    }
-
-    fn group_active(
-        mut self,
-        group_name: impl Into<SharedString>,
-        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateful_interactivity().group_active_style = Some(GroupStyle {
-            group: group_name.into(),
-            style: f(StyleRefinement::default()),
-        });
-        self
-    }
-
-    fn on_click(
-        mut self,
-        listener: impl Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.stateful_interactivity()
-            .click_listeners
-            .push(Box::new(move |view, event, cx| listener(view, event, cx)));
-        self
-    }
-
-    fn on_drag<W>(
-        mut self,
-        listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-        W: 'static + Render,
-    {
-        debug_assert!(
-            self.stateful_interactivity().drag_listener.is_none(),
-            "calling on_drag more than once on the same element is not supported"
-        );
-        self.stateful_interactivity().drag_listener =
-            Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
-                view: listener(view_state, cx).into(),
-                cursor_offset,
-            }));
-        self
-    }
-
-    fn on_hover(mut self, listener: impl 'static + Fn(&mut V, bool, &mut ViewContext<V>)) -> Self
-    where
-        Self: Sized,
-    {
-        debug_assert!(
-            self.stateful_interactivity().hover_listener.is_none(),
-            "calling on_hover more than once on the same element is not supported"
-        );
-        self.stateful_interactivity().hover_listener = Some(Box::new(listener));
-        self
-    }
-
-    fn tooltip<W>(
-        mut self,
-        build_tooltip: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-        W: 'static + Render,
-    {
-        debug_assert!(
-            self.stateful_interactivity().tooltip_builder.is_none(),
-            "calling tooltip more than once on the same element is not supported"
-        );
-        self.stateful_interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
-            build_tooltip(view_state, cx).into()
-        }));
-
-        self
-    }
-}
-
-pub trait ElementInteractivity<V: 'static>: 'static {
-    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 refine_style(
-        &self,
-        style: &mut Style,
-        bounds: Bounds<Pixels>,
-        element_state: &InteractiveElementState,
-        cx: &mut ViewContext<V>,
-    ) {
-        let mouse_position = cx.mouse_position();
-        let stateless = self.as_stateless();
-        if let Some(group_hover) = stateless.group_hover_style.as_ref() {
-            if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
-                if group_bounds.contains_point(&mouse_position) {
-                    style.refine(&group_hover.style);
-                }
-            }
-        }
-        if bounds.contains_point(&mouse_position) {
-            style.refine(&stateless.hover_style);
-        }
-
-        if let Some(drag) = cx.active_drag.take() {
-            for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
-                if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
-                    if *state_type == drag.view.entity_type()
-                        && group_bounds.contains_point(&mouse_position)
-                    {
-                        style.refine(&group_drag_style.style);
-                    }
-                }
-            }
-
-            for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
-                if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
-                {
-                    style.refine(drag_over_style);
-                }
-            }
-
-            cx.active_drag = Some(drag);
-        }
-
-        if let Some(stateful) = self.as_stateful() {
-            let active_state = element_state.active_state.lock();
-            if active_state.group {
-                if let Some(group_style) = stateful.group_active_style.as_ref() {
-                    style.refine(&group_style.style);
-                }
-            }
-            if active_state.element {
-                style.refine(&stateful.active_style);
-            }
-        }
-    }
-
-    fn initialize(&mut self, cx: &mut ViewContext<V>) {
-        let stateless = self.as_stateless_mut();
-
-        for listener in stateless.key_down_listeners.drain(..) {
-            cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
-                listener(state, event, phase, cx);
-            })
-        }
-
-        for listener in stateless.key_up_listeners.drain(..) {
-            cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
-                listener(state, event, phase, cx);
-            })
-        }
-
-        for (action_type, listener) in stateless.action_listeners.drain(..) {
-            cx.on_action(action_type, listener)
-        }
-    }
-
-    fn handle_events(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        content_size: Size<Pixels>,
-        overflow: Point<Overflow>,
-        element_state: &mut InteractiveElementState,
-        cx: &mut ViewContext<V>,
-    ) {
-        let stateless = self.as_stateless_mut();
-        for listener in stateless.mouse_down_listeners.drain(..) {
-            cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
-                listener(state, event, &bounds, phase, cx);
-            })
-        }
-
-        for listener in stateless.mouse_up_listeners.drain(..) {
-            cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
-                listener(state, event, &bounds, phase, cx);
-            })
-        }
-
-        for listener in stateless.mouse_move_listeners.drain(..) {
-            cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
-                listener(state, event, &bounds, phase, cx);
-            })
-        }
-
-        for listener in stateless.scroll_wheel_listeners.drain(..) {
-            cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
-                listener(state, event, &bounds, phase, cx);
-            })
-        }
-
-        let hover_group_bounds = stateless
-            .group_hover_style
-            .as_ref()
-            .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
-
-        if let Some(group_bounds) = hover_group_bounds {
-            let hovered = group_bounds.contains_point(&cx.mouse_position());
-            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
-                if phase == DispatchPhase::Capture {
-                    if group_bounds.contains_point(&event.position) != hovered {
-                        cx.notify();
-                    }
-                }
-            });
-        }
-
-        if stateless.hover_style.is_some()
-            || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty())
-        {
-            let hovered = bounds.contains_point(&cx.mouse_position());
-            cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
-                if phase == DispatchPhase::Capture {
-                    if bounds.contains_point(&event.position) != hovered {
-                        cx.notify();
-                    }
-                }
-            });
-        }
-
-        if cx.active_drag.is_some() {
-            let drop_listeners = mem::take(&mut stateless.drop_listeners);
-            cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
-                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                    if let Some(drag_state_type) =
-                        cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
-                    {
-                        for (drop_state_type, listener) in &drop_listeners {
-                            if *drop_state_type == drag_state_type {
-                                let drag = cx
-                                    .active_drag
-                                    .take()
-                                    .expect("checked for type drag state type above");
-                                listener(view, drag.view.clone(), cx);
-                                cx.notify();
-                                cx.stop_propagation();
-                            }
-                        }
-                    }
-                }
-            });
-        }
-
-        if let Some(stateful) = self.as_stateful_mut() {
-            let click_listeners = mem::take(&mut stateful.click_listeners);
-            let drag_listener = mem::take(&mut stateful.drag_listener);
-
-            if !click_listeners.is_empty() || drag_listener.is_some() {
-                let pending_mouse_down = element_state.pending_mouse_down.clone();
-                let mouse_down = pending_mouse_down.lock().clone();
-                if let Some(mouse_down) = mouse_down {
-                    if let Some(drag_listener) = drag_listener {
-                        let active_state = element_state.active_state.clone();
-
-                        cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
-                            if cx.active_drag.is_some() {
-                                if phase == DispatchPhase::Capture {
-                                    cx.notify();
-                                }
-                            } else if phase == DispatchPhase::Bubble
-                                && bounds.contains_point(&event.position)
-                                && (event.position - mouse_down.position).magnitude()
-                                    > DRAG_THRESHOLD
-                            {
-                                *active_state.lock() = ActiveState::default();
-                                let cursor_offset = event.position - bounds.origin;
-                                let drag = drag_listener(view_state, cursor_offset, cx);
-                                cx.active_drag = Some(drag);
-                                cx.notify();
-                                cx.stop_propagation();
-                            }
-                        });
-                    }
-
-                    cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| {
-                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
-                        {
-                            let mouse_click = ClickEvent {
-                                down: mouse_down.clone(),
-                                up: event.clone(),
-                            };
-                            for listener in &click_listeners {
-                                listener(view_state, &mouse_click, cx);
-                            }
-                        }
-                        *pending_mouse_down.lock() = None;
-                    });
-                } else {
-                    cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| {
-                        if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position)
-                        {
-                            *pending_mouse_down.lock() = Some(event.clone());
-                        }
-                    });
-                }
-            }
-
-            if let Some(hover_listener) = stateful.hover_listener.take() {
-                let was_hovered = element_state.hover_state.clone();
-                let has_mouse_down = element_state.pending_mouse_down.clone();
-
-                cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| {
-                    if phase != DispatchPhase::Bubble {
-                        return;
-                    }
-                    let is_hovered =
-                        bounds.contains_point(&event.position) && has_mouse_down.lock().is_none();
-                    let mut was_hovered = was_hovered.lock();
-
-                    if is_hovered != was_hovered.clone() {
-                        *was_hovered = is_hovered;
-                        drop(was_hovered);
-
-                        hover_listener(view_state, is_hovered, cx);
-                    }
-                });
-            }
-
-            if let Some(tooltip_builder) = stateful.tooltip_builder.take() {
-                let active_tooltip = element_state.active_tooltip.clone();
-                let pending_mouse_down = element_state.pending_mouse_down.clone();
-
-                cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| {
-                    if phase != DispatchPhase::Bubble {
-                        return;
-                    }
-
-                    let is_hovered = bounds.contains_point(&event.position)
-                        && pending_mouse_down.lock().is_none();
-                    if !is_hovered {
-                        active_tooltip.lock().take();
-                        return;
-                    }
-
-                    if active_tooltip.lock().is_none() {
-                        let task = cx.spawn({
-                            let active_tooltip = active_tooltip.clone();
-                            let tooltip_builder = tooltip_builder.clone();
-
-                            move |view, mut cx| async move {
-                                cx.background_executor().timer(TOOLTIP_DELAY).await;
-                                view.update(&mut cx, move |view_state, cx| {
-                                    active_tooltip.lock().replace(ActiveTooltip {
-                                        waiting: None,
-                                        tooltip: Some(AnyTooltip {
-                                            view: tooltip_builder(view_state, cx),
-                                            cursor_offset: cx.mouse_position() + TOOLTIP_OFFSET,
-                                        }),
-                                    });
-                                    cx.notify();
-                                })
-                                .ok();
-                            }
-                        });
-                        active_tooltip.lock().replace(ActiveTooltip {
-                            waiting: Some(task),
-                            tooltip: None,
-                        });
-                    }
-                });
-
-                if let Some(active_tooltip) = element_state.active_tooltip.lock().as_ref() {
-                    if active_tooltip.tooltip.is_some() {
-                        cx.active_tooltip = active_tooltip.tooltip.clone()
-                    }
-                }
-            }
-
-            let active_state = element_state.active_state.clone();
-            if active_state.lock().is_none() {
-                let active_group_bounds = stateful
-                    .group_active_style
-                    .as_ref()
-                    .and_then(|group_active| GroupBounds::get(&group_active.group, cx));
-                cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| {
-                    if phase == DispatchPhase::Bubble {
-                        let group = active_group_bounds
-                            .map_or(false, |bounds| bounds.contains_point(&down.position));
-                        let element = bounds.contains_point(&down.position);
-                        if group || element {
-                            *active_state.lock() = ActiveState { group, element };
-                            cx.notify();
-                        }
-                    }
-                });
-            } else {
-                cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| {
-                    if phase == DispatchPhase::Capture {
-                        *active_state.lock() = ActiveState::default();
-                        cx.notify();
-                    }
-                });
-            }
-
-            if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
-                let scroll_offset = element_state
-                    .scroll_offset
-                    .get_or_insert_with(Arc::default)
-                    .clone();
-                let line_height = cx.line_height();
-                let scroll_max = (content_size - bounds.size).max(&Size::default());
-
-                cx.on_mouse_event(move |_, event: &ScrollWheelEvent, phase, cx| {
-                    if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
-                        let mut scroll_offset = scroll_offset.lock();
-                        let old_scroll_offset = *scroll_offset;
-                        let delta = event.delta.pixel_delta(line_height);
-
-                        if overflow.x == Overflow::Scroll {
-                            scroll_offset.x =
-                                (scroll_offset.x + delta.x).clamp(-scroll_max.width, px(0.));
-                        }
-
-                        if overflow.y == Overflow::Scroll {
-                            scroll_offset.y =
-                                (scroll_offset.y + delta.y).clamp(-scroll_max.height, px(0.));
-                        }
-
-                        if *scroll_offset != old_scroll_offset {
-                            cx.notify();
-                            cx.stop_propagation();
-                        }
-                    }
-                });
-            }
-        }
-    }
-}
-
-#[derive(Deref, DerefMut)]
-pub struct StatefulInteractivity<V> {
-    pub id: ElementId,
-    #[deref]
-    #[deref_mut]
-    stateless: StatelessInteractivity<V>,
-    click_listeners: SmallVec<[ClickListener<V>; 2]>,
-    active_style: StyleRefinement,
-    group_active_style: Option<GroupStyle>,
-    drag_listener: Option<DragListener<V>>,
-    hover_listener: Option<HoverListener<V>>,
-    tooltip_builder: Option<TooltipBuilder<V>>,
-}
-
-impl<V: 'static> StatefulInteractivity<V> {
-    pub fn new(id: ElementId, stateless: StatelessInteractivity<V>) -> Self {
-        Self {
-            id,
-            stateless,
-            click_listeners: SmallVec::new(),
-            active_style: StyleRefinement::default(),
-            group_active_style: None,
-            drag_listener: None,
-            hover_listener: None,
-            tooltip_builder: None,
-        }
-    }
-}
-
-impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
-    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
-    }
-}
-
-type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
-
-pub struct StatelessInteractivity<V> {
-    pub dispatch_context: KeyContext,
-    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_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
-    pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
-    pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
-    pub hover_style: StyleRefinement,
-    pub group_hover_style: Option<GroupStyle>,
-    drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
-    group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>,
-    drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
-}
-
-impl<V> StatelessInteractivity<V> {
-    pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteractivity<V> {
-        StatefulInteractivity {
-            id: id.into(),
-            stateless: self,
-            click_listeners: SmallVec::new(),
-            drag_listener: None,
-            hover_listener: None,
-            tooltip_builder: None,
-            active_style: StyleRefinement::default(),
-            group_active_style: None,
-        }
-    }
-}
-
-pub struct GroupStyle {
-    pub group: SharedString,
-    pub style: StyleRefinement,
-}
-
-#[derive(Default)]
-pub struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
-
-impl GroupBounds {
-    pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
-        cx.default_global::<Self>()
-            .0
-            .get(name)
-            .and_then(|bounds_stack| bounds_stack.last())
-            .cloned()
-    }
-
-    pub fn push(name: SharedString, bounds: Bounds<Pixels>, cx: &mut AppContext) {
-        cx.default_global::<Self>()
-            .0
-            .entry(name)
-            .or_default()
-            .push(bounds);
-    }
-
-    pub fn pop(name: &SharedString, cx: &mut AppContext) {
-        cx.default_global::<Self>().0.get_mut(name).unwrap().pop();
-    }
-}
-
-#[derive(Copy, Clone, Default, Eq, PartialEq)]
-struct ActiveState {
-    pub group: bool,
-    pub element: bool,
-}
-
-impl ActiveState {
-    pub fn is_none(&self) -> bool {
-        !self.group && !self.element
-    }
-}
-
-#[derive(Default)]
-pub struct InteractiveElementState {
-    active_state: Arc<Mutex<ActiveState>>,
-    hover_state: Arc<Mutex<bool>>,
-    pending_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
-    scroll_offset: Option<Arc<Mutex<Point<Pixels>>>>,
-    active_tooltip: Arc<Mutex<Option<ActiveTooltip>>>,
-}
-
-struct ActiveTooltip {
-    #[allow(unused)] // used to drop the task
-    waiting: Option<Task<()>>,
-    tooltip: Option<AnyTooltip>,
-}
-
-impl InteractiveElementState {
-    pub fn scroll_offset(&self) -> Option<Point<Pixels>> {
-        self.scroll_offset
-            .as_ref()
-            .map(|offset| offset.lock().clone())
-    }
-
-    pub fn track_scroll_offset(&mut self) -> Arc<Mutex<Point<Pixels>>> {
-        self.scroll_offset
-            .get_or_insert_with(|| Arc::new(Mutex::new(Default::default())))
-            .clone()
-    }
-}
-
-impl<V> Default for StatelessInteractivity<V> {
-    fn default() -> Self {
-        Self {
-            dispatch_context: KeyContext::default(),
-            mouse_down_listeners: SmallVec::new(),
-            mouse_up_listeners: SmallVec::new(),
-            mouse_move_listeners: SmallVec::new(),
-            scroll_wheel_listeners: SmallVec::new(),
-            key_down_listeners: SmallVec::new(),
-            key_up_listeners: SmallVec::new(),
-            action_listeners: SmallVec::new(),
-            hover_style: StyleRefinement::default(),
-            group_hover_style: None,
-            drag_over_styles: SmallVec::new(),
-            group_drag_over_styles: SmallVec::new(),
-            drop_listeners: SmallVec::new(),
-        }
-    }
-}
-
-impl<V: 'static> ElementInteractivity<V> for StatelessInteractivity<V> {
-    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
-    }
-}
-
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct KeyDownEvent {
     pub keystroke: Keystroke,
@@ -991,10 +93,6 @@ where
     }
 }
 
-// impl<S, R, V, E> Render for Drag<S, R, V, E> {
-//     // fn render(&mut self, cx: ViewContext<Self>) ->
-// }
-
 #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
 pub enum MouseButton {
     Left,
@@ -1103,7 +201,7 @@ impl Deref for MouseExitEvent {
 pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
 
 impl Render for ExternalPaths {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
         div() // Intentionally left empty because the platform will render icons for the dragged files
@@ -1229,8 +327,8 @@ pub type ActionListener<V> =
 #[cfg(test)]
 mod test {
     use crate::{
-        self as gpui, div, Div, FocusHandle, KeyBinding, Keystroke, ParentElement, Render,
-        StatefulInteractivity, StatelessInteractive, TestAppContext, VisualContext,
+        self as gpui, div, FocusHandle, InteractiveComponent, KeyBinding, Keystroke, Node,
+        ParentComponent, Render, Stateful, TestAppContext, VisualContext,
     };
 
     struct TestView {
@@ -1242,12 +340,12 @@ mod test {
     actions!(TestAction);
 
     impl Render for TestView {
-        type Element = Div<Self, StatefulInteractivity<Self>>;
+        type Element = Stateful<Self, Node<Self>>;
 
         fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
             div().id("testview").child(
                 div()
-                    .context("test")
+                    .key_context("test")
                     .track_focus(&self.focus_handle)
                     .on_key_down(|this: &mut TestView, _, _, _| this.saw_key_down = true)
                     .on_action(|this: &mut TestView, _: &TestAction, _| this.saw_action = true),

crates/gpui2/src/key_dispatch.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle,
-    FocusId, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels,
-    Style, StyleRefinement, ViewContext, WindowContext,
+    build_action_from_type, Action, Bounds, DispatchPhase, FocusEvent, FocusHandle, FocusId,
+    KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels, Style,
+    StyleRefinement, ViewContext, WindowContext,
 };
 use collections::HashMap;
 use parking_lot::Mutex;
@@ -342,115 +342,3 @@ impl<V: 'static> KeyDispatch<V> for NonFocusableKeyDispatch {
         &mut self.key_context
     }
 }
-
-pub trait Focusable<V: 'static>: Element<V> {
-    fn focus_listeners(&mut self) -> &mut FocusListeners<V>;
-    fn set_focus_style(&mut self, style: StyleRefinement);
-    fn set_focus_in_style(&mut self, style: StyleRefinement);
-    fn set_in_focus_style(&mut self, style: StyleRefinement);
-
-    fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.set_focus_style(f(StyleRefinement::default()));
-        self
-    }
-
-    fn focus_in(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.set_focus_in_style(f(StyleRefinement::default()));
-        self
-    }
-
-    fn in_focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
-    where
-        Self: Sized,
-    {
-        self.set_in_focus_style(f(StyleRefinement::default()));
-        self
-    }
-
-    fn on_focus(
-        mut self,
-        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.focus_listeners()
-            .push(Box::new(move |view, focus_handle, event, cx| {
-                if event.focused.as_ref() == Some(focus_handle) {
-                    listener(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_blur(
-        mut self,
-        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.focus_listeners()
-            .push(Box::new(move |view, focus_handle, event, cx| {
-                if event.blurred.as_ref() == Some(focus_handle) {
-                    listener(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_focus_in(
-        mut self,
-        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.focus_listeners()
-            .push(Box::new(move |view, focus_handle, event, cx| {
-                let descendant_blurred = event
-                    .blurred
-                    .as_ref()
-                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
-                let descendant_focused = event
-                    .focused
-                    .as_ref()
-                    .map_or(false, |focused| focus_handle.contains(focused, cx));
-
-                if !descendant_blurred && descendant_focused {
-                    listener(view, event, cx)
-                }
-            }));
-        self
-    }
-
-    fn on_focus_out(
-        mut self,
-        listener: impl Fn(&mut V, &FocusEvent, &mut ViewContext<V>) + 'static,
-    ) -> Self
-    where
-        Self: Sized,
-    {
-        self.focus_listeners()
-            .push(Box::new(move |view, focus_handle, event, cx| {
-                let descendant_blurred = event
-                    .blurred
-                    .as_ref()
-                    .map_or(false, |blurred| focus_handle.contains(blurred, cx));
-                let descendant_focused = event
-                    .focused
-                    .as_ref()
-                    .map_or(false, |focused| focus_handle.contains(focused, cx));
-                if descendant_blurred && !descendant_focused {
-                    listener(view, event, cx)
-                }
-            }));
-        self
-    }
-}

crates/gpui2/src/prelude.rs 🔗

@@ -1 +1,4 @@
-pub use crate::{Context, ParentElement, Refineable};
+pub use crate::{
+    BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
+    ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
+};

crates/gpui2/src/styled.rs 🔗

@@ -6,21 +6,20 @@ use crate::{
 use crate::{BoxShadow, TextStyleRefinement};
 use refineable::Refineable;
 use smallvec::{smallvec, SmallVec};
+use taffy::style::Overflow;
 
-pub trait Styled {
+pub trait Styled: Sized {
     fn style(&mut self) -> &mut StyleRefinement;
 
-    fn computed_style(&mut self) -> Style {
-        Style::default().refined(self.style().clone())
-    }
-
     gpui2_macros::style_helpers!();
 
+    fn z_index(mut self, z_index: u32) -> Self {
+        self.style().z_index = Some(z_index);
+        self
+    }
+
     /// Sets the size of the element to the full width and height.
-    fn full(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn full(mut self) -> Self {
         self.style().size.width = Some(relative(1.).into());
         self.style().size.height = Some(relative(1.).into());
         self
@@ -28,118 +27,98 @@ pub trait Styled {
 
     /// Sets the position of the element to `relative`.
     /// [Docs](https://tailwindcss.com/docs/position)
-    fn relative(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn relative(mut self) -> Self {
         self.style().position = Some(Position::Relative);
         self
     }
 
     /// Sets the position of the element to `absolute`.
     /// [Docs](https://tailwindcss.com/docs/position)
-    fn absolute(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn absolute(mut self) -> Self {
         self.style().position = Some(Position::Absolute);
         self
     }
 
     /// Sets the display type of the element to `block`.
     /// [Docs](https://tailwindcss.com/docs/display)
-    fn block(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn block(mut self) -> Self {
         self.style().display = Some(Display::Block);
         self
     }
 
     /// Sets the display type of the element to `flex`.
     /// [Docs](https://tailwindcss.com/docs/display)
-    fn flex(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex(mut self) -> Self {
         self.style().display = Some(Display::Flex);
         self
     }
 
     /// Sets the visibility of the element to `visible`.
     /// [Docs](https://tailwindcss.com/docs/visibility)
-    fn visible(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn visible(mut self) -> Self {
         self.style().visibility = Some(Visibility::Visible);
         self
     }
 
     /// Sets the visibility of the element to `hidden`.
     /// [Docs](https://tailwindcss.com/docs/visibility)
-    fn invisible(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn invisible(mut self) -> Self {
         self.style().visibility = Some(Visibility::Hidden);
         self
     }
 
-    fn cursor(mut self, cursor: CursorStyle) -> Self
-    where
-        Self: Sized,
-    {
+    fn overflow_hidden(mut self) -> Self {
+        self.style().overflow.x = Some(Overflow::Hidden);
+        self.style().overflow.y = Some(Overflow::Hidden);
+        self
+    }
+
+    fn overflow_hidden_x(mut self) -> Self {
+        self.style().overflow.x = Some(Overflow::Hidden);
+        self
+    }
+
+    fn overflow_hidden_y(mut self) -> Self {
+        self.style().overflow.y = Some(Overflow::Hidden);
+        self
+    }
+
+    fn cursor(mut self, cursor: CursorStyle) -> Self {
         self.style().mouse_cursor = Some(cursor);
         self
     }
 
     /// Sets the cursor style when hovering an element to `default`.
     /// [Docs](https://tailwindcss.com/docs/cursor)
-    fn cursor_default(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn cursor_default(mut self) -> Self {
         self.style().mouse_cursor = Some(CursorStyle::Arrow);
         self
     }
 
     /// Sets the cursor style when hovering an element to `pointer`.
     /// [Docs](https://tailwindcss.com/docs/cursor)
-    fn cursor_pointer(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn cursor_pointer(mut self) -> Self {
         self.style().mouse_cursor = Some(CursorStyle::PointingHand);
         self
     }
 
     /// Sets the flex direction of the element to `column`.
     /// [Docs](https://tailwindcss.com/docs/flex-direction#column)
-    fn flex_col(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_col(mut self) -> Self {
         self.style().flex_direction = Some(FlexDirection::Column);
         self
     }
 
     /// Sets the flex direction of the element to `row`.
     /// [Docs](https://tailwindcss.com/docs/flex-direction#row)
-    fn flex_row(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_row(mut self) -> Self {
         self.style().flex_direction = Some(FlexDirection::Row);
         self
     }
 
     /// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size.
     /// [Docs](https://tailwindcss.com/docs/flex#flex-1)
-    fn flex_1(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_1(mut self) -> Self {
         self.style().flex_grow = Some(1.);
         self.style().flex_shrink = Some(1.);
         self.style().flex_basis = Some(relative(0.).into());
@@ -148,10 +127,7 @@ pub trait Styled {
 
     /// Sets the element to allow a flex item to grow and shrink, taking into account its initial size.
     /// [Docs](https://tailwindcss.com/docs/flex#auto)
-    fn flex_auto(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_auto(mut self) -> Self {
         self.style().flex_grow = Some(1.);
         self.style().flex_shrink = Some(1.);
         self.style().flex_basis = Some(Length::Auto);
@@ -160,10 +136,7 @@ pub trait Styled {
 
     /// Sets the element to allow a flex item to shrink but not grow, taking into account its initial size.
     /// [Docs](https://tailwindcss.com/docs/flex#initial)
-    fn flex_initial(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_initial(mut self) -> Self {
         self.style().flex_grow = Some(0.);
         self.style().flex_shrink = Some(1.);
         self.style().flex_basis = Some(Length::Auto);
@@ -172,10 +145,7 @@ pub trait Styled {
 
     /// Sets the element to prevent a flex item from growing or shrinking.
     /// [Docs](https://tailwindcss.com/docs/flex#none)
-    fn flex_none(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn flex_none(mut self) -> Self {
         self.style().flex_grow = Some(0.);
         self.style().flex_shrink = Some(0.);
         self
@@ -183,40 +153,28 @@ pub trait Styled {
 
     /// Sets the element to allow a flex item to grow to fill any available space.
     /// [Docs](https://tailwindcss.com/docs/flex-grow)
-    fn grow(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn grow(mut self) -> Self {
         self.style().flex_grow = Some(1.);
         self
     }
 
     /// Sets the element to align flex items to the start of the container's cross axis.
     /// [Docs](https://tailwindcss.com/docs/align-items#start)
-    fn items_start(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn items_start(mut self) -> Self {
         self.style().align_items = Some(AlignItems::FlexStart);
         self
     }
 
     /// Sets the element to align flex items to the end of the container's cross axis.
     /// [Docs](https://tailwindcss.com/docs/align-items#end)
-    fn items_end(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn items_end(mut self) -> Self {
         self.style().align_items = Some(AlignItems::FlexEnd);
         self
     }
 
     /// Sets the element to align flex items along the center of the container's cross axis.
     /// [Docs](https://tailwindcss.com/docs/align-items#center)
-    fn items_center(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn items_center(mut self) -> Self {
         self.style().align_items = Some(AlignItems::Center);
         self
     }
@@ -224,40 +182,28 @@ pub trait Styled {
     /// Sets the element to justify flex items along the container's main axis
     /// such that there is an equal amount of space between each item.
     /// [Docs](https://tailwindcss.com/docs/justify-content#space-between)
-    fn justify_between(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn justify_between(mut self) -> Self {
         self.style().justify_content = Some(JustifyContent::SpaceBetween);
         self
     }
 
     /// Sets the element to justify flex items along the center of the container's main axis.
     /// [Docs](https://tailwindcss.com/docs/justify-content#center)
-    fn justify_center(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn justify_center(mut self) -> Self {
         self.style().justify_content = Some(JustifyContent::Center);
         self
     }
 
     /// Sets the element to justify flex items against the start of the container's main axis.
     /// [Docs](https://tailwindcss.com/docs/justify-content#start)
-    fn justify_start(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn justify_start(mut self) -> Self {
         self.style().justify_content = Some(JustifyContent::Start);
         self
     }
 
     /// Sets the element to justify flex items against the end of the container's main axis.
     /// [Docs](https://tailwindcss.com/docs/justify-content#end)
-    fn justify_end(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn justify_end(mut self) -> Self {
         self.style().justify_content = Some(JustifyContent::End);
         self
     }
@@ -265,10 +211,7 @@ pub trait Styled {
     /// Sets the element to justify items along the container's main axis such
     /// that there is an equal amount of space on each side of each item.
     /// [Docs](https://tailwindcss.com/docs/justify-content#space-around)
-    fn justify_around(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn justify_around(mut self) -> Self {
         self.style().justify_content = Some(JustifyContent::SpaceAround);
         self
     }
@@ -295,30 +238,21 @@ pub trait Styled {
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self {
         self.style().box_shadow = Some(shadows);
         self
     }
 
     /// Clears the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_none(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_none(mut self) -> Self {
         self.style().box_shadow = Some(Default::default());
         self
     }
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_sm(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_sm(mut self) -> Self {
         self.style().box_shadow = Some(smallvec::smallvec![BoxShadow {
             color: hsla(0., 0., 0., 0.05),
             offset: point(px(0.), px(1.)),
@@ -330,10 +264,7 @@ pub trait Styled {
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_md(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_md(mut self) -> Self {
         self.style().box_shadow = Some(smallvec![
             BoxShadow {
                 color: hsla(0.5, 0., 0., 0.1),
@@ -353,10 +284,7 @@ pub trait Styled {
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_lg(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_lg(mut self) -> Self {
         self.style().box_shadow = Some(smallvec![
             BoxShadow {
                 color: hsla(0., 0., 0., 0.1),
@@ -376,10 +304,7 @@ pub trait Styled {
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_xl(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_xl(mut self) -> Self {
         self.style().box_shadow = Some(smallvec![
             BoxShadow {
                 color: hsla(0., 0., 0., 0.1),
@@ -399,10 +324,7 @@ pub trait Styled {
 
     /// Sets the box shadow of the element.
     /// [Docs](https://tailwindcss.com/docs/box-shadow)
-    fn shadow_2xl(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn shadow_2xl(mut self) -> Self {
         self.style().box_shadow = Some(smallvec![BoxShadow {
             color: hsla(0., 0., 0., 0.25),
             offset: point(px(0.), px(25.)),
@@ -417,198 +339,138 @@ pub trait Styled {
         &mut style.text
     }
 
-    fn text_color(mut self, color: impl Into<Hsla>) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_color(mut self, color: impl Into<Hsla>) -> Self {
         self.text_style().get_or_insert_with(Default::default).color = Some(color.into());
         self
     }
 
-    fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(size.into());
         self
     }
 
-    fn text_xs(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_xs(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(0.75).into());
         self
     }
 
-    fn text_sm(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_sm(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(0.875).into());
         self
     }
 
-    fn text_base(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_base(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(1.0).into());
         self
     }
 
-    fn text_lg(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_lg(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(1.125).into());
         self
     }
 
-    fn text_xl(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_xl(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(1.25).into());
         self
     }
 
-    fn text_2xl(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_2xl(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(1.5).into());
         self
     }
 
-    fn text_3xl(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_3xl(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_size = Some(rems(1.875).into());
         self
     }
 
-    fn text_decoration_none(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_none(mut self) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .underline = None;
         self
     }
 
-    fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.color = Some(color.into());
         self
     }
 
-    fn text_decoration_solid(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_solid(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.wavy = false;
         self
     }
 
-    fn text_decoration_wavy(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_wavy(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.wavy = true;
         self
     }
 
-    fn text_decoration_0(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_0(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.thickness = px(0.);
         self
     }
 
-    fn text_decoration_1(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_1(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.thickness = px(1.);
         self
     }
 
-    fn text_decoration_2(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_2(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.thickness = px(2.);
         self
     }
 
-    fn text_decoration_4(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_4(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.thickness = px(4.);
         self
     }
 
-    fn text_decoration_8(mut self) -> Self
-    where
-        Self: Sized,
-    {
+    fn text_decoration_8(mut self) -> Self {
         let style = self.text_style().get_or_insert_with(Default::default);
         let underline = style.underline.get_or_insert_with(Default::default);
         underline.thickness = px(8.);
         self
     }
 
-    fn font(mut self, family_name: impl Into<SharedString>) -> Self
-    where
-        Self: Sized,
-    {
+    fn font(mut self, family_name: impl Into<SharedString>) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .font_family = Some(family_name.into());
         self
     }
 
-    fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self
-    where
-        Self: Sized,
-    {
+    fn line_height(mut self, line_height: impl Into<DefiniteLength>) -> Self {
         self.text_style()
             .get_or_insert_with(Default::default)
             .line_height = Some(line_height.into());

crates/gpui2/src/view.rs 🔗

@@ -206,7 +206,7 @@ impl<V: Render> From<View<V>> for AnyView {
 impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
     type ElementState = Box<dyn Any>;
 
-    fn id(&self) -> Option<ElementId> {
+    fn element_id(&self) -> Option<ElementId> {
         Some(self.model.entity_id.into())
     }
 

crates/gpui2_macros/src/style_helpers.rs 🔗

@@ -130,7 +130,7 @@ fn generate_predefined_setter(
 
     let method = quote! {
         #[doc = #doc_string]
-        fn #method_name(mut self) -> Self where Self: std::marker::Sized {
+        fn #method_name(mut self) -> Self {
             let style = self.style();
             #(#field_assignments)*
             self
@@ -163,7 +163,7 @@ fn generate_custom_value_setter(
 
     let method = quote! {
         #[doc = #doc_string]
-        fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self where Self: std::marker::Sized {
+        fn #method_name(mut self, length: impl std::clone::Clone + Into<gpui::#length_type>) -> Self {
             let style = self.style();
             #(#field_assignments)*
             self

crates/picker2/src/picker2.rs 🔗

@@ -1,6 +1,6 @@
 use editor::Editor;
 use gpui::{
-    div, uniform_list, Component, Div, ParentElement, Render, StatelessInteractive, Styled, Task,
+    div, uniform_list, Component, Node, ParentComponent, Render, Styled, Task,
     UniformListScrollHandle, View, ViewContext, VisualContext, WindowContext,
 };
 use std::{cmp, sync::Arc};
@@ -139,11 +139,11 @@ impl<D: PickerDelegate> Picker<D> {
 }
 
 impl<D: PickerDelegate> Render for Picker<D> {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div()
-            .context("picker")
+            .key_context("picker")
             .size_full()
             .elevation_2(cx)
             .on_action(Self::select_next)

crates/storybook2/src/stories/colors.rs 🔗

@@ -1,12 +1,12 @@
 use crate::story::Story;
-use gpui::{px, Div, Render};
+use gpui::{prelude::*, px, Node, Render};
 use theme2::{default_color_scales, ColorScaleStep};
 use ui::prelude::*;
 
 pub struct ColorsStory;
 
 impl Render for ColorsStory {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let color_scales = default_color_scales();

crates/storybook2/src/stories/focus.rs 🔗

@@ -1,6 +1,5 @@
 use gpui::{
-    actions, div, Div, FocusHandle, Focusable, FocusableKeyDispatch, KeyBinding, ParentElement,
-    Render, StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext,
+    actions, div, prelude::*, FocusHandle, Focusable, KeyBinding, Node, Render, Stateful, View,
     WindowContext,
 };
 use theme2::ActiveTheme;
@@ -28,7 +27,7 @@ impl FocusStory {
 }
 
 impl Render for FocusStory {
-    type Element = Div<Self, StatefulInteractivity<Self>, FocusableKeyDispatch<Self>>;
+    type Element = Focusable<Self, Stateful<Self, Node<Self>>>;
 
     fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
         let theme = cx.theme();
@@ -42,7 +41,7 @@ impl Render for FocusStory {
         div()
             .id("parent")
             .focusable()
-            .context("parent")
+            .key_context("parent")
             .on_action(|_, action: &ActionA, cx| {
                 println!("Action A dispatched on parent");
             })
@@ -62,7 +61,7 @@ impl Render for FocusStory {
             .child(
                 div()
                     .track_focus(&self.child_1_focus)
-                    .context("child-1")
+                    .key_context("child-1")
                     .on_action(|_, action: &ActionB, cx| {
                         println!("Action B dispatched on child 1 during");
                     })
@@ -82,7 +81,7 @@ impl Render for FocusStory {
             .child(
                 div()
                     .track_focus(&self.child_2_focus)
-                    .context("child-2")
+                    .key_context("child-2")
                     .on_action(|_, action: &ActionC, cx| {
                         println!("Action C dispatched on child 2");
                     })

crates/storybook2/src/stories/kitchen_sink.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{story::Story, story_selector::ComponentStory};
-use gpui::{Div, Render, StatefulInteractivity, View, VisualContext};
+use gpui::{prelude::*, Node, Render, Stateful, View};
 use strum::IntoEnumIterator;
 use ui::prelude::*;
 
@@ -12,7 +12,7 @@ impl KitchenSinkStory {
 }
 
 impl Render for KitchenSinkStory {
-    type Element = Div<Self, StatefulInteractivity<Self>>;
+    type Element = Stateful<Self, Node<Self>>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let component_stories = ComponentStory::iter()

crates/storybook2/src/stories/picker.rs 🔗

@@ -1,11 +1,7 @@
-use std::sync::Arc;
-
 use fuzzy::StringMatchCandidate;
-use gpui::{
-    div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
-    View, VisualContext, WindowContext,
-};
+use gpui::{div, prelude::*, KeyBinding, Node, Render, Styled, Task, View, WindowContext};
 use picker::{Picker, PickerDelegate};
+use std::sync::Arc;
 use theme2::ActiveTheme;
 
 pub struct PickerStory {
@@ -38,7 +34,7 @@ impl Delegate {
 }
 
 impl PickerDelegate for Delegate {
-    type ListItem = Div<Picker<Self>>;
+    type ListItem = Node<Picker<Self>>;
 
     fn match_count(&self) -> usize {
         self.candidates.len()
@@ -207,7 +203,7 @@ impl PickerStory {
 }
 
 impl Render for PickerStory {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
         div()

crates/storybook2/src/stories/scroll.rs 🔗

@@ -1,6 +1,5 @@
 use gpui::{
-    div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
-    View, VisualContext, WindowContext,
+    div, prelude::*, px, Node, Render, SharedString, Stateful, Styled, View, WindowContext,
 };
 use theme2::ActiveTheme;
 
@@ -13,7 +12,7 @@ impl ScrollStory {
 }
 
 impl Render for ScrollStory {
-    type Element = Div<Self, StatefulInteractivity<Self>>;
+    type Element = Stateful<Self, Node<Self>>;
 
     fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
         let theme = cx.theme();

crates/storybook2/src/stories/text.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext};
+use gpui::{div, white, Node, ParentComponent, Render, Styled, View, VisualContext, WindowContext};
 
 pub struct TextStory;
 
@@ -9,7 +9,7 @@ impl TextStory {
 }
 
 impl Render for TextStory {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
         div().size_full().bg(white()).child(concat!(

crates/storybook2/src/stories/z_index.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{px, rgb, Div, Hsla, Render};
+use gpui::{px, rgb, Hsla, Node, Render};
 use ui::prelude::*;
 
 use crate::story::Story;
@@ -8,7 +8,7 @@ use crate::story::Story;
 pub struct ZIndexStory;
 
 impl Render for ZIndexStory {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         Story::container(cx)
@@ -77,7 +77,7 @@ trait Styles: Styled + Sized {
     }
 }
 
-impl<V: 'static> Styles for Div<V> {}
+impl<V: 'static> Styles for Node<V> {}
 
 #[derive(Component)]
 struct ZIndexExample {

crates/storybook2/src/storybook2.rs 🔗

@@ -9,7 +9,7 @@ use std::sync::Arc;
 
 use clap::Parser;
 use gpui::{
-    div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
+    div, px, size, AnyView, AppContext, Bounds, Node, Render, ViewContext, VisualContext,
     WindowBounds, WindowOptions,
 };
 use log::LevelFilter;
@@ -107,7 +107,7 @@ impl StoryWrapper {
 }
 
 impl Render for StoryWrapper {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div()

crates/theme2/src/players.rs 🔗

@@ -40,12 +40,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{ActiveTheme, Story};
-    use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
+    use gpui::{div, img, px, Node, ParentComponent, Render, Styled, ViewContext};
 
     pub struct PlayerStory;
 
     impl Render for PlayerStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx).child(

crates/theme2/src/story.rs 🔗

@@ -1,11 +1,11 @@
-use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
+use gpui::{div, Component, Node, ParentComponent, Styled, ViewContext};
 
 use crate::ActiveTheme;
 
 pub struct Story {}
 
 impl Story {
-    pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
+    pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
         div()
             .size_full()
             .flex()

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

@@ -44,12 +44,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct AvatarStory;
 
     impl Render for AvatarStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -1,6 +1,6 @@
 use std::sync::Arc;
 
-use gpui::{div, DefiniteLength, Hsla, MouseButton, WindowContext};
+use gpui::{div, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
 
 use crate::{
     h_stack, prelude::*, Icon, IconButton, IconColor, IconElement, Label, LabelColor,
@@ -236,13 +236,13 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{h_stack, v_stack, LabelColor, Story};
-    use gpui::{rems, Div, Render};
+    use gpui::{rems, Node, Render};
     use strum::IntoEnumIterator;
 
     pub struct ButtonStory;
 
     impl Render for ButtonStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             let states = InteractionState::iter();

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

@@ -1,9 +1,5 @@
+use gpui::{div, prelude::*, Component, ElementId, Styled, ViewContext};
 use std::sync::Arc;
-
-use gpui::{
-    div, Component, ElementId, ParentElement, StatefulInteractive, StatelessInteractive, Styled,
-    ViewContext,
-};
 use theme2::ActiveTheme;
 
 use crate::{Icon, IconColor, IconElement, Selection};
@@ -175,12 +171,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{h_stack, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct CheckboxStory;
 
     impl Render for CheckboxStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -65,12 +65,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::story::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct ContextMenuStory;
 
     impl Render for ContextMenuStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -47,12 +47,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{Button, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct DetailsStory;
 
     impl Render for DetailsStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -1,11 +1,11 @@
-use gpui::Div;
+use gpui::Node;
 
 use crate::{prelude::*, v_stack};
 
 /// Create an elevated surface.
 ///
 /// Must be used inside of a relative parent element
-pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Div<V> {
+pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Node<V> {
     let colors = cx.theme().colors();
 
     // let shadow = BoxShadow {
@@ -23,6 +23,6 @@ pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<
         .shadow(level.shadow())
 }
 
-pub fn modal<V>(cx: &mut ViewContext<V>) -> Div<V> {
+pub fn modal<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
     elevated_surface(ElevationIndex::ModalSurface, cx)
 }

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

@@ -33,12 +33,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{static_players, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct FacepileStory;
 
     impl Render for FacepileStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             let players = static_players();

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

@@ -204,7 +204,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
     use strum::IntoEnumIterator;
 
     use crate::Story;
@@ -214,7 +214,7 @@ mod stories {
     pub struct IconStory;
 
     impl Render for IconStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             let icons = Icon::iter();

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

@@ -1,9 +1,7 @@
-use std::sync::Arc;
-
-use gpui::{rems, MouseButton};
-
 use crate::{h_stack, prelude::*};
 use crate::{ClickHandler, Icon, IconColor, IconElement};
+use gpui::{prelude::*, rems, MouseButton};
+use std::sync::Arc;
 
 struct IconButtonHandlers<V: 'static> {
     click: Option<ClickHandler<V>>,

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

@@ -1,6 +1,5 @@
-use crate::prelude::*;
-use crate::Label;
-use crate::LabelColor;
+use crate::{prelude::*, Label, LabelColor};
+use gpui::prelude::*;
 
 #[derive(Default, PartialEq)]
 pub enum InputVariant {
@@ -111,12 +110,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct InputStory;
 
     impl Render for InputStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -158,13 +158,13 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
     use itertools::Itertools;
 
     pub struct KeybindingStory;
 
     impl Render for KeybindingStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             let all_modifier_permutations = ModifierKey::iter().permutations(2);

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

@@ -196,12 +196,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct LabelStory;
 
     impl Render for LabelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -74,7 +74,7 @@ impl<V: 'static> Modal<V> {
     }
 }
 
-impl<V: 'static> ParentElement<V> for Modal<V> {
+impl<V: 'static> ParentComponent<V> for Modal<V> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         &mut self.children
     }

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

@@ -1,5 +1,5 @@
-use crate::prelude::*;
-use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
+use crate::{h_stack, prelude::*, v_stack, Keybinding, Label, LabelColor};
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct Palette {
@@ -159,7 +159,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::{ModifierKeys, Story};
 
@@ -168,7 +168,7 @@ mod stories {
     pub struct PaletteStory;
 
     impl Render for PaletteStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             {

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

@@ -1,4 +1,4 @@
-use gpui::{AbsoluteLength, AnyElement};
+use gpui::{prelude::*, AbsoluteLength, AnyElement};
 use smallvec::SmallVec;
 
 use crate::prelude::*;
@@ -113,7 +113,7 @@ impl<V: 'static> Panel<V> {
     }
 }
 
-impl<V: 'static> ParentElement<V> for Panel<V> {
+impl<V: 'static> ParentComponent<V> for Panel<V> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         &mut self.children
     }
@@ -126,12 +126,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{Label, Story};
-    use gpui::{Div, Render};
+    use gpui::{InteractiveComponent, Node, Render};
 
     pub struct PanelStory;
 
     impl Render for PanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -1,17 +1,17 @@
-use gpui::{div, Div};
+use gpui::{div, Node};
 
 use crate::StyledExt;
 
 /// Horizontally stacks elements.
 ///
 /// Sets `flex()`, `flex_row()`, `items_center()`
-pub fn h_stack<V: 'static>() -> Div<V> {
+pub fn h_stack<V: 'static>() -> Node<V> {
     div().h_flex()
 }
 
 /// Vertically stacks elements.
 ///
 /// Sets `flex()`, `flex_col()`
-pub fn v_stack<V: 'static>() -> Div<V> {
+pub fn v_stack<V: 'static>() -> Node<V> {
     div().v_flex()
 }

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

@@ -1,6 +1,6 @@
 use crate::prelude::*;
 use crate::{Icon, IconColor, IconElement, Label, LabelColor};
-use gpui::{red, Div, ElementId, Render, View, VisualContext};
+use gpui::{prelude::*, red, ElementId, Node, Render, View};
 
 #[derive(Component, Clone)]
 pub struct Tab {
@@ -21,7 +21,7 @@ struct TabDragState {
 }
 
 impl Render for TabDragState {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div().w_8().h_4().bg(red())
@@ -178,7 +178,7 @@ mod stories {
     pub struct TabStory;
 
     impl Render for TabStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             let git_statuses = GitStatus::iter();

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

@@ -1,7 +1,6 @@
-use gpui::AnyElement;
-use smallvec::SmallVec;
-
 use crate::prelude::*;
+use gpui::{prelude::*, AnyElement};
+use smallvec::SmallVec;
 
 #[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
 pub enum ToastOrigin {
@@ -59,7 +58,7 @@ impl<V: 'static> Toast<V> {
     }
 }
 
-impl<V: 'static> ParentElement<V> for Toast<V> {
+impl<V: 'static> ParentComponent<V> for Toast<V> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         &mut self.children
     }
@@ -70,7 +69,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::{Label, Story};
 
@@ -79,7 +78,7 @@ mod stories {
     pub struct ToastStory;
 
     impl Render for ToastStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -1,4 +1,4 @@
-use gpui::{div, Component, ParentElement};
+use gpui::{div, Component, ParentComponent};
 
 use crate::{Icon, IconColor, IconElement, IconSize};
 

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

@@ -1,4 +1,4 @@
-use gpui::{div, Div, ParentElement, Render, SharedString, Styled, ViewContext};
+use gpui::{div, Node, ParentComponent, Render, SharedString, Styled, ViewContext};
 use theme2::ActiveTheme;
 
 #[derive(Clone, Debug)]
@@ -13,7 +13,7 @@ impl TextTooltip {
 }
 
 impl Render for TextTooltip {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let theme = cx.theme();

crates/ui2/src/prelude.rs 🔗

@@ -1,8 +1,8 @@
 use gpui::rems;
 use gpui::Rems;
 pub use gpui::{
-    div, Component, Element, ElementId, ParentElement, SharedString, StatefulInteractive,
-    StatelessInteractive, Styled, ViewContext, WindowContext,
+    div, Component, Element, ElementId, InteractiveComponent, ParentComponent, SharedString, Styled,
+    ViewContext, WindowContext,
 };
 
 pub use crate::elevation::*;

crates/ui2/src/story.rs 🔗

@@ -1,11 +1,11 @@
-use gpui::Div;
+use gpui::Node;
 
 use crate::prelude::*;
 
 pub struct Story {}
 
 impl Story {
-    pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
+    pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Node<V> {
         div()
             .size_full()
             .flex()

crates/ui2/src/styled_ext.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Div, ElementInteractivity, KeyDispatch, Styled, UniformList, ViewContext};
+use gpui::{Styled, ViewContext};
 use theme2::ActiveTheme;
 
 use crate::{ElevationIndex, UITextSize};
@@ -93,11 +93,4 @@ pub trait StyledExt: Styled + Sized {
     }
 }
 
-impl<V, I, F> StyledExt for Div<V, I, F>
-where
-    I: ElementInteractivity<V>,
-    F: KeyDispatch<V>,
-{
-}
-
-impl<V> StyledExt for UniformList<V> {}
+impl<E: Styled> StyledExt for E {}

crates/ui2/src/to_extract/assistant_panel.rs 🔗

@@ -1,6 +1,6 @@
 use crate::prelude::*;
 use crate::{Icon, IconButton, Label, Panel, PanelSide};
-use gpui::{rems, AbsoluteLength};
+use gpui::{prelude::*, rems, AbsoluteLength};
 
 #[derive(Component)]
 pub struct AssistantPanel {
@@ -77,11 +77,11 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
     pub struct AssistantPanelStory;
 
     impl Render for AssistantPanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/breadcrumb.rs 🔗

@@ -1,9 +1,7 @@
+use crate::{h_stack, prelude::*, HighlightedText};
+use gpui::{prelude::*, Node};
 use std::path::PathBuf;
 
-use crate::prelude::*;
-use crate::{h_stack, HighlightedText};
-use gpui::Div;
-
 #[derive(Clone)]
 pub struct Symbol(pub Vec<HighlightedText>);
 
@@ -18,7 +16,7 @@ impl Breadcrumb {
         Self { path, symbols }
     }
 
-    fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
+    fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Node<V> {
         div()
             .child(" › ")
             .text_color(cx.theme().colors().text_muted)
@@ -79,7 +77,7 @@ mod stories {
     pub struct BreadcrumbStory;
 
     impl Render for BreadcrumbStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/buffer.rs 🔗

@@ -235,12 +235,12 @@ mod stories {
         empty_buffer_example, hello_world_rust_buffer_example,
         hello_world_rust_buffer_with_status_example, Story,
     };
-    use gpui::{rems, Div, Render};
+    use gpui::{rems, Node, Render};
 
     pub struct BufferStory;
 
     impl Render for BufferStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/buffer_search.rs 🔗

@@ -1,4 +1,4 @@
-use gpui::{Div, Render, View, VisualContext};
+use gpui::{Node, Render, View, VisualContext};
 
 use crate::prelude::*;
 use crate::{h_stack, Icon, IconButton, IconColor, Input};
@@ -27,9 +27,9 @@ impl BufferSearch {
 }
 
 impl Render for BufferSearch {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
         h_stack()
             .bg(cx.theme().colors().toolbar_background)
             .p_2()

crates/ui2/src/to_extract/chat_panel.rs 🔗

@@ -1,7 +1,6 @@
+use crate::{prelude::*, Icon, IconButton, Input, Label, LabelColor};
 use chrono::NaiveDateTime;
-
-use crate::prelude::*;
-use crate::{Icon, IconButton, Input, Label, LabelColor};
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct ChatPanel {
@@ -108,7 +107,7 @@ pub use stories::*;
 #[cfg(feature = "stories")]
 mod stories {
     use chrono::DateTime;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::{Panel, Story};
 
@@ -117,7 +116,7 @@ mod stories {
     pub struct ChatPanelStory;
 
     impl Render for ChatPanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/collab_panel.rs 🔗

@@ -1,7 +1,8 @@
-use crate::{prelude::*, Toggle};
 use crate::{
-    static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, ListHeader,
+    prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
+    List, ListHeader, Toggle,
 };
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct CollabPanel {
@@ -92,12 +93,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct CollabPanelStory;
 
     impl Render for CollabPanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/command_palette.rs 🔗

@@ -27,7 +27,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::Story;
 
@@ -36,7 +36,7 @@ mod stories {
     pub struct CommandPaletteStory;
 
     impl Render for CommandPaletteStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/copilot.rs 🔗

@@ -25,7 +25,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::Story;
 
@@ -34,7 +34,7 @@ mod stories {
     pub struct CopilotModalStory;
 
     impl Render for CopilotModalStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/editor_pane.rs 🔗

@@ -1,6 +1,6 @@
 use std::path::PathBuf;
 
-use gpui::{Div, Render, View, VisualContext};
+use gpui::{Node, Render, View, VisualContext};
 
 use crate::prelude::*;
 use crate::{
@@ -48,9 +48,9 @@ impl EditorPane {
 }
 
 impl Render for EditorPane {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
         v_stack()
             .w_full()
             .h_full()

crates/ui2/src/to_extract/language_selector.rs 🔗

@@ -40,12 +40,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct LanguageSelectorStory;
 
     impl Render for LanguageSelectorStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/multi_buffer.rs 🔗

@@ -40,12 +40,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{hello_world_rust_buffer_example, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct MultiBufferStory;
 
     impl Render for MultiBufferStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/notifications_panel.rs 🔗

@@ -1,10 +1,9 @@
-use crate::utils::naive_format_distance_from_now;
 use crate::{
-    h_stack, prelude::*, static_new_notification_items_2, v_stack, Avatar, ButtonOrIconButton,
-    Icon, IconElement, Label, LabelColor, LineHeightStyle, ListHeaderMeta, ListSeparator,
-    PublicPlayer, UnreadIndicator,
+    h_stack, prelude::*, static_new_notification_items_2, utils::naive_format_distance_from_now,
+    v_stack, Avatar, ButtonOrIconButton, ClickHandler, Icon, IconElement, Label, LabelColor,
+    LineHeightStyle, ListHeader, ListHeaderMeta, ListSeparator, PublicPlayer, UnreadIndicator,
 };
-use crate::{ClickHandler, ListHeader};
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct NotificationsPanel {
@@ -353,12 +352,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{Panel, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct NotificationsPanelStory;
 
     impl Render for NotificationsPanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

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

@@ -59,7 +59,7 @@ impl<V: 'static> Pane<V> {
     }
 }
 
-impl<V: 'static> ParentElement<V> for Pane<V> {
+impl<V: 'static> ParentComponent<V> for Pane<V> {
     fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
         &mut self.children
     }

crates/ui2/src/to_extract/project_panel.rs 🔗

@@ -1,7 +1,8 @@
-use crate::prelude::*;
 use crate::{
-    static_project_panel_project_items, static_project_panel_single_items, Input, List, ListHeader,
+    prelude::*, static_project_panel_project_items, static_project_panel_single_items, Input, List,
+    ListHeader,
 };
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct ProjectPanel {
@@ -54,12 +55,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::{Panel, Story};
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct ProjectPanelStory;
 
     impl Render for ProjectPanelStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/recent_projects.rs 🔗

@@ -36,12 +36,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct RecentProjectsStory;
 
     impl Render for RecentProjectsStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/tab_bar.rs 🔗

@@ -1,5 +1,5 @@
-use crate::prelude::*;
-use crate::{Icon, IconButton, Tab};
+use crate::{prelude::*, Icon, IconButton, Tab};
+use gpui::prelude::*;
 
 #[derive(Component)]
 pub struct TabBar {
@@ -100,12 +100,12 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     pub struct TabBarStory;
 
     impl Render for TabBarStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/terminal.rs 🔗

@@ -83,11 +83,11 @@ pub use stories::*;
 mod stories {
     use super::*;
     use crate::Story;
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
     pub struct TerminalStory;
 
     impl Render for TerminalStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/theme_selector.rs 🔗

@@ -39,7 +39,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::Story;
 
@@ -48,7 +48,7 @@ mod stories {
     pub struct ThemeSelectorStory;
 
     impl Render for ThemeSelectorStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/title_bar.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::atomic::AtomicBool;
 use std::sync::Arc;
 
-use gpui::{Div, Render, View, VisualContext};
+use gpui::{Node, Render, View, VisualContext};
 
 use crate::prelude::*;
 use crate::settings::user_settings;
@@ -86,9 +86,9 @@ impl TitleBar {
 }
 
 impl Render for TitleBar {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
         let settings = user_settings(cx);
 
         // let has_focus = cx.window_is_active();
@@ -202,9 +202,9 @@ mod stories {
     }
 
     impl Render for TitleBarStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
-        fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
+        fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
             Story::container(cx)
                 .child(Story::title_for::<_, TitleBar>(cx))
                 .child(Story::label(cx, "Default"))

crates/ui2/src/to_extract/toolbar.rs 🔗

@@ -73,7 +73,7 @@ mod stories {
     use std::path::PathBuf;
     use std::str::FromStr;
 
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
 
@@ -82,7 +82,7 @@ mod stories {
     pub struct ToolbarStory;
 
     impl Render for ToolbarStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/traffic_lights.rs 🔗

@@ -77,7 +77,7 @@ pub use stories::*;
 
 #[cfg(feature = "stories")]
 mod stories {
-    use gpui::{Div, Render};
+    use gpui::{Node, Render};
 
     use crate::Story;
 
@@ -86,7 +86,7 @@ mod stories {
     pub struct TrafficLightsStory;
 
     impl Render for TrafficLightsStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             Story::container(cx)

crates/ui2/src/to_extract/workspace.rs 🔗

@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use chrono::DateTime;
-use gpui::{px, relative, Div, Render, Size, View, VisualContext};
+use gpui::{px, relative, Node, Render, Size, View, VisualContext};
 use settings2::Settings;
 use theme2::ThemeSettings;
 
@@ -192,9 +192,9 @@ impl Workspace {
 }
 
 impl Render for Workspace {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
-    fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Node<Self> {
         let root_group = PaneGroup::new_panes(
             vec![Pane::new(
                 "pane-0",
@@ -388,7 +388,7 @@ mod stories {
     }
 
     impl Render for WorkspaceStory {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             div().child(self.workspace.clone())

crates/workspace2/src/dock.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{status_bar::StatusItemView, Axis, Workspace};
 use gpui::{
-    div, Action, AnyView, AppContext, Div, Entity, EntityId, EventEmitter, ParentElement, Render,
-    Subscription, View, ViewContext, WeakView, WindowContext,
+    div, Action, AnyView, AppContext, Entity, EntityId, EventEmitter, Node, ParentComponent,
+    Render, Subscription, View, ViewContext, WeakView, WindowContext,
 };
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -419,7 +419,7 @@ impl Dock {
 }
 
 impl Render for Dock {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         todo!()
@@ -621,7 +621,7 @@ impl PanelButtons {
 // }
 
 impl Render for PanelButtons {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         // todo!()
@@ -647,7 +647,7 @@ impl StatusItemView for PanelButtons {
 #[cfg(any(test, feature = "test-support"))]
 pub mod test {
     use super::*;
-    use gpui::{div, Div, ViewContext, WindowContext};
+    use gpui::{div, Node, ViewContext, WindowContext};
 
     pub struct TestPanel {
         pub position: DockPosition,
@@ -672,7 +672,7 @@ pub mod test {
     }
 
     impl Render for TestPanel {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
             div()

crates/workspace2/src/modal_layer.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
-    Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
+    div, prelude::*, px, AnyView, EventEmitter, FocusHandle, InteractiveComponent, Node,
+    ParentComponent, Render, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
 };
 use ui::v_stack;
 
@@ -76,7 +76,7 @@ impl ModalLayer {
 }
 
 impl Render for ModalLayer {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let Some(active_modal) = &self.active_modal else {

crates/workspace2/src/notifications.rs 🔗

@@ -165,7 +165,7 @@ impl Workspace {
 
 pub mod simple_message_notification {
     use super::{Notification, NotificationEvent};
-    use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
+    use gpui::{AnyElement, AppContext, EventEmitter, Node, Render, TextStyle, ViewContext};
     use serde::Deserialize;
     use std::{borrow::Cow, sync::Arc};
 
@@ -252,7 +252,7 @@ pub mod simple_message_notification {
     }
 
     impl Render for MessageNotification {
-        type Element = Div<Self>;
+        type Element = Node<Self>;
 
         fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
             todo!()

crates/workspace2/src/pane.rs 🔗

@@ -1,5 +1,3 @@
-// mod dragged_item_receiver;
-
 use crate::{
     item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
     toolbar::Toolbar,
@@ -9,9 +7,9 @@ use crate::{
 use anyhow::Result;
 use collections::{HashMap, HashSet, VecDeque};
 use gpui::{
-    actions, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
-    EventEmitter, FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext,
-    WeakView, WindowContext,
+    actions, prelude::*, register_action, AppContext, AsyncWindowContext, Component, EntityId,
+    EventEmitter, FocusHandle, Model, Node, PromptLevel, Render, Task, View, ViewContext,
+    VisualContext, WeakView, WindowContext,
 };
 use parking_lot::Mutex;
 use project2::{Project, ProjectEntryId, ProjectPath};
@@ -1903,7 +1901,7 @@ impl Pane {
 // }
 
 impl Render for Pane {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         v_stack()
@@ -2928,7 +2926,7 @@ struct DraggedTab {
 }
 
 impl Render for DraggedTab {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div().w_8().h_4().bg(gpui::red())

crates/workspace2/src/status_bar.rs 🔗

@@ -2,8 +2,8 @@ use std::any::TypeId;
 
 use crate::{ItemHandle, Pane};
 use gpui::{
-    div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View, ViewContext,
-    WindowContext,
+    div, AnyView, Component, Node, ParentComponent, Render, Styled, Subscription, View,
+    ViewContext, WindowContext,
 };
 use theme2::ActiveTheme;
 use util::ResultExt;
@@ -34,7 +34,7 @@ pub struct StatusBar {
 }
 
 impl Render for StatusBar {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         div()

crates/workspace2/src/toolbar.rs 🔗

@@ -1,6 +1,6 @@
 use crate::ItemHandle;
 use gpui::{
-    AnyView, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
+    AnyView, Entity, EntityId, EventEmitter, Node, Render, View, ViewContext, WindowContext,
 };
 
 pub enum ToolbarItemEvent {
@@ -52,7 +52,7 @@ pub struct Toolbar {
 }
 
 impl Render for Toolbar {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         todo!()

crates/workspace2/src/workspace2.rs 🔗

@@ -36,12 +36,11 @@ use futures::{
     Future, FutureExt, StreamExt,
 };
 use gpui::{
-    actions, div, point, rems, size, Action, AnyModel, AnyView, AnyWeakView, AppContext,
-    AsyncAppContext, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter,
-    FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render, Size,
-    StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task,
-    View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
-    WindowOptions,
+    actions, div, point, prelude::*, rems, size, Action, AnyModel, AnyView, AnyWeakView,
+    AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Component, Entity, EntityId,
+    EventEmitter, FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, Node,
+    ParentComponent, Point, Render, Size, Styled, Subscription, Task, View, ViewContext, WeakView,
+    WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -443,7 +442,6 @@ struct Follower {
 impl AppState {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test(cx: &mut AppContext) -> Arc<Self> {
-        use gpui::Context;
         use node_runtime::FakeNodeRuntime;
         use settings2::SettingsStore;
 
@@ -531,13 +529,7 @@ pub enum Event {
 pub struct Workspace {
     weak_self: WeakView<Self>,
     focus_handle: FocusHandle,
-    workspace_actions: Vec<
-        Box<
-            dyn Fn(
-                Div<Workspace, StatelessInteractivity<Workspace>>,
-            ) -> Div<Workspace, StatelessInteractivity<Workspace>>,
-        >,
-    >,
+    workspace_actions: Vec<Box<dyn Fn(Node<Workspace>) -> Node<Workspace>>>,
     zoomed: Option<AnyWeakView>,
     zoomed_position: Option<DockPosition>,
     center: PaneGroup,
@@ -3450,7 +3442,6 @@ impl Workspace {
 
     #[cfg(any(test, feature = "test-support"))]
     pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
-        use gpui::Context;
         use node_runtime::FakeNodeRuntime;
 
         let client = project.read(cx).client();
@@ -3512,10 +3503,7 @@ impl Workspace {
         }));
     }
 
-    fn add_workspace_actions_listeners(
-        &self,
-        mut div: Div<Workspace, StatelessInteractivity<Workspace>>,
-    ) -> Div<Workspace, StatelessInteractivity<Workspace>> {
+    fn add_workspace_actions_listeners(&self, mut div: Node<Workspace>) -> Node<Workspace> {
         for action in self.workspace_actions.iter() {
             div = (action)(div)
         }
@@ -3740,14 +3728,14 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
 impl EventEmitter<Event> for Workspace {}
 
 impl Render for Workspace {
-    type Element = Div<Self>;
+    type Element = Node<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let mut context = KeyContext::default();
         context.add("Workspace");
 
         self.add_workspace_actions_listeners(div())
-            .context(context)
+            .key_context(context)
             .relative()
             .size_full()
             .flex()