Move self in Element::paint

Nathan Sobo created

Remove mutable state borrows in favor of state ownership in render processes to streamline element rendering.

Change summary

crates/editor2/src/element.rs             |  20 +-
crates/gpui2/src/app.rs                   |  10 
crates/gpui2/src/app/entity_map.rs        |  10 
crates/gpui2/src/element.rs               | 219 ++++++++++++++----------
crates/gpui2/src/elements/div.rs          |  58 +++---
crates/gpui2/src/elements/img.rs          |  10 
crates/gpui2/src/elements/overlay.rs      |  12 
crates/gpui2/src/elements/svg.rs          |  10 
crates/gpui2/src/elements/text.rs         |  36 ++--
crates/gpui2/src/elements/uniform_list.rs |  27 +-
crates/gpui2/src/gpui2.rs                 |   2 
crates/gpui2/src/view.rs                  |  53 ++---
crates/gpui2/src/window.rs                |   4 
crates/gpui2_macros/src/gpui2_macros.rs   |   6 
crates/ui2/src/components/context_menu.rs |  16 
15 files changed, 262 insertions(+), 231 deletions(-)

Detailed changes

crates/editor2/src/element.rs 🔗

@@ -488,8 +488,8 @@ impl EditorElement {
             }
         }
 
-        for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() {
-            if let Some(fold_indicator) = fold_indicator.as_mut() {
+        for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
+            if let Some(mut fold_indicator) = fold_indicator {
                 let available_space = size(
                     AvailableSpace::MinContent,
                     AvailableSpace::Definite(line_height * 0.55),
@@ -509,7 +509,7 @@ impl EditorElement {
             }
         }
 
-        if let Some(indicator) = layout.code_actions_indicator.as_mut() {
+        if let Some(mut indicator) = layout.code_actions_indicator.take() {
             let available_space = size(
                 AvailableSpace::MinContent,
                 AvailableSpace::Definite(line_height),
@@ -840,7 +840,7 @@ impl EditorElement {
                     }
                 });
 
-                if let Some((position, context_menu)) = layout.context_menu.as_mut() {
+                if let Some((position, mut context_menu)) = layout.context_menu.take() {
                     cx.with_z_index(1, |cx| {
                         let line_height = self.style.text.line_height_in_pixels(cx.rem_size());
                         let available_space = size(
@@ -1224,7 +1224,7 @@ impl EditorElement {
         let scroll_left = scroll_position.x * layout.position_map.em_width;
         let scroll_top = scroll_position.y * layout.position_map.line_height;
 
-        for block in &mut layout.blocks {
+        for block in layout.blocks.drain(..) {
             let mut origin = bounds.origin
                 + point(
                     Pixels::ZERO,
@@ -2389,7 +2389,7 @@ enum Invisible {
 }
 
 impl Element<Editor> for EditorElement {
-    type ElementState = ();
+    type State = ();
 
     fn element_id(&self) -> Option<gpui::ElementId> {
         Some(self.editor_id.into())
@@ -2398,9 +2398,9 @@ impl Element<Editor> for EditorElement {
     fn layout(
         &mut self,
         editor: &mut Editor,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut gpui::ViewContext<Editor>,
-    ) -> (gpui::LayoutId, Self::ElementState) {
+    ) -> (gpui::LayoutId, Self::State) {
         editor.style = Some(self.style.clone()); // Long-term, we'd like to eliminate this.
 
         let rem_size = cx.rem_size();
@@ -2416,10 +2416,10 @@ impl Element<Editor> for EditorElement {
     }
 
     fn paint(
-        &mut self,
+        mut self,
         bounds: Bounds<gpui::Pixels>,
         editor: &mut Editor,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut gpui::ViewContext<Editor>,
     ) {
         let mut layout = self.compute_layout(editor, cx, bounds);

crates/gpui2/src/app.rs 🔗

@@ -14,7 +14,7 @@ use smallvec::SmallVec;
 pub use test_context::*;
 
 use crate::{
-    current_platform, image_cache::ImageCache, Action, ActionRegistry, AnyBox, AnyView,
+    current_platform, image_cache::ImageCache, Action, ActionRegistry, Any, AnyView,
     AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
     DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
     ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform,
@@ -28,7 +28,7 @@ use futures::{channel::oneshot, future::LocalBoxFuture, Future};
 use parking_lot::Mutex;
 use slotmap::SlotMap;
 use std::{
-    any::{type_name, Any, TypeId},
+    any::{type_name, TypeId},
     cell::{Ref, RefCell, RefMut},
     marker::PhantomData,
     mem,
@@ -194,7 +194,7 @@ pub struct AppContext {
     asset_source: Arc<dyn AssetSource>,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
+    pub(crate) globals_by_type: HashMap<TypeId, Box<dyn Any>>,
     pub(crate) entities: EntityMap,
     pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
@@ -1100,12 +1100,12 @@ pub(crate) enum Effect {
 
 /// Wraps a global variable value during `update_global` while the value has been moved to the stack.
 pub(crate) struct GlobalLease<G: 'static> {
-    global: AnyBox,
+    global: Box<dyn Any>,
     global_type: PhantomData<G>,
 }
 
 impl<G: 'static> GlobalLease<G> {
-    fn new(global: AnyBox) -> Self {
+    fn new(global: Box<dyn Any>) -> Self {
         GlobalLease {
             global,
             global_type: PhantomData,

crates/gpui2/src/app/entity_map.rs 🔗

@@ -1,10 +1,10 @@
-use crate::{private::Sealed, AnyBox, AppContext, Context, Entity, ModelContext};
+use crate::{private::Sealed, AppContext, Context, Entity, ModelContext};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 use slotmap::{SecondaryMap, SlotMap};
 use std::{
-    any::{type_name, TypeId},
+    any::{type_name, Any, TypeId},
     fmt::{self, Display},
     hash::{Hash, Hasher},
     marker::PhantomData,
@@ -31,7 +31,7 @@ impl Display for EntityId {
 }
 
 pub(crate) struct EntityMap {
-    entities: SecondaryMap<EntityId, AnyBox>,
+    entities: SecondaryMap<EntityId, Box<dyn Any>>,
     ref_counts: Arc<RwLock<EntityRefCounts>>,
 }
 
@@ -101,7 +101,7 @@ impl EntityMap {
         );
     }
 
-    pub fn take_dropped(&mut self) -> Vec<(EntityId, AnyBox)> {
+    pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
         let mut ref_counts = self.ref_counts.write();
         let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids);
 
@@ -121,7 +121,7 @@ impl EntityMap {
 }
 
 pub struct Lease<'a, T> {
-    entity: Option<AnyBox>,
+    entity: Option<Box<dyn Any>>,
     pub model: &'a Model<T>,
     entity_type: PhantomData<T>,
 }

crates/gpui2/src/element.rs 🔗

@@ -3,25 +3,25 @@ use crate::{
 };
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug, mem};
+use std::{any::Any, fmt::Debug};
 
 pub trait Element<V: 'static>: 'static + Sized {
-    type ElementState: 'static;
+    type State: 'static;
 
     fn element_id(&self) -> Option<ElementId>;
 
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState);
+    ) -> (LayoutId, Self::State);
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     );
 
@@ -35,32 +35,26 @@ pub trait Element<V: 'static>: 'static + Sized {
         available_space: Size<T>,
         view_state: &mut V,
         cx: &mut ViewContext<V>,
-        f: impl FnOnce(&Self::ElementState, &mut ViewContext<V>) -> R,
+        f: impl FnOnce(&mut Self::State, &mut ViewContext<V>) -> R,
     ) -> R
     where
         T: Clone + Default + Debug + Into<AvailableSpace>,
     {
-        let mut element = RenderedElement {
-            element: self,
-            phase: ElementRenderPhase::Start,
+        let element_id = self.element_id();
+        let element = DrawableElement {
+            element: Some(self),
+            phase: ElementDrawPhase::Start,
         };
-        element.draw(origin, available_space.map(Into::into), view_state, cx);
-        if let ElementRenderPhase::Painted { frame_state } = &element.phase {
-            if let Some(frame_state) = frame_state.as_ref() {
-                f(&frame_state, cx)
-            } else {
-                let element_id = element
-                    .element
-                    .element_id()
-                    .expect("we either have some frame_state or some element_id");
-                cx.with_element_state(element_id, |element_state, cx| {
-                    let element_state = element_state.unwrap();
-                    let result = f(&element_state, cx);
-                    (result, element_state)
-                })
-            }
+        let frame_state = element.draw(origin, available_space.map(Into::into), view_state, cx);
+
+        if let Some(mut frame_state) = frame_state {
+            f(&mut frame_state, cx)
         } else {
-            unreachable!()
+            cx.with_element_state(element_id.unwrap(), |element_state, cx| {
+                let mut element_state = element_state.unwrap();
+                let result = f(&mut element_state, cx);
+                (result, element_state)
+            })
         }
     }
 }
@@ -107,13 +101,13 @@ trait ElementObject<V> {
     );
 }
 
-struct RenderedElement<V: 'static, E: Element<V>> {
-    element: E,
-    phase: ElementRenderPhase<E::ElementState>,
+pub struct DrawableElement<V: 'static, E: Element<V>> {
+    element: Option<E>,
+    phase: ElementDrawPhase<E::State>,
 }
 
 #[derive(Default)]
-enum ElementRenderPhase<V> {
+enum ElementDrawPhase<V> {
     #[default]
     Start,
     LayoutRequested {
@@ -125,83 +119,83 @@ enum ElementRenderPhase<V> {
         available_space: Size<AvailableSpace>,
         frame_state: Option<V>,
     },
-    Painted {
-        frame_state: Option<V>,
-    },
 }
 
 /// Internal struct that wraps an element to store Layout and ElementState after the element is rendered.
 /// It's allocated as a trait object to erase the element type and wrapped in AnyElement<E::State> for
 /// improved usability.
-impl<V, E: Element<V>> RenderedElement<V, E> {
+impl<V, E: Element<V>> DrawableElement<V, E> {
     fn new(element: E) -> Self {
-        RenderedElement {
-            element,
-            phase: ElementRenderPhase::Start,
+        DrawableElement {
+            element: Some(element),
+            phase: ElementDrawPhase::Start,
         }
     }
-}
 
-impl<V, E> ElementObject<V> for RenderedElement<V, E>
-where
-    E: Element<V>,
-    E::ElementState: 'static,
-{
     fn layout(&mut self, state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
-        let (layout_id, frame_state) = match mem::take(&mut self.phase) {
-            ElementRenderPhase::Start => {
-                if let Some(id) = self.element.element_id() {
-                    let layout_id = cx.with_element_state(id, |element_state, cx| {
-                        self.element.layout(state, element_state, cx)
-                    });
-                    (layout_id, None)
-                } else {
-                    let (layout_id, frame_state) = self.element.layout(state, None, cx);
-                    (layout_id, Some(frame_state))
-                }
-            }
-            ElementRenderPhase::LayoutRequested { .. }
-            | ElementRenderPhase::LayoutComputed { .. }
-            | ElementRenderPhase::Painted { .. } => {
-                panic!("element rendered twice")
-            }
+        let (layout_id, frame_state) = if let Some(id) = self.element.as_ref().unwrap().element_id()
+        {
+            let layout_id = cx.with_element_state(id, |element_state, cx| {
+                self.element
+                    .as_mut()
+                    .unwrap()
+                    .layout(state, element_state, cx)
+            });
+            (layout_id, None)
+        } else {
+            let (layout_id, frame_state) = self.element.as_mut().unwrap().layout(state, None, cx);
+            (layout_id, Some(frame_state))
         };
 
-        self.phase = ElementRenderPhase::LayoutRequested {
+        self.phase = ElementDrawPhase::LayoutRequested {
             layout_id,
             frame_state,
         };
         layout_id
     }
 
-    fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
-        self.phase = match mem::take(&mut self.phase) {
-            ElementRenderPhase::LayoutRequested {
+    fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> Option<E::State> {
+        match self.phase {
+            ElementDrawPhase::LayoutRequested {
                 layout_id,
-                mut frame_state,
+                frame_state,
             }
-            | ElementRenderPhase::LayoutComputed {
+            | ElementDrawPhase::LayoutComputed {
                 layout_id,
-                mut frame_state,
+                frame_state,
                 ..
             } => {
                 let bounds = cx.layout_bounds(layout_id);
-                if let Some(id) = self.element.element_id() {
-                    cx.with_element_state(id, |element_state, cx| {
+
+                if let Some(mut frame_state) = frame_state {
+                    self.element
+                        .take()
+                        .unwrap()
+                        .paint(bounds, view_state, &mut frame_state, cx);
+                    Some(frame_state)
+                } else {
+                    let element_id = self
+                        .element
+                        .as_ref()
+                        .unwrap()
+                        .element_id()
+                        .expect("if we don't have frame state, we should have element state");
+                    cx.with_element_state(element_id, |element_state, cx| {
                         let mut element_state = element_state.unwrap();
-                        self.element
-                            .paint(bounds, view_state, &mut element_state, cx);
+                        self.element.take().unwrap().paint(
+                            bounds,
+                            view_state,
+                            &mut element_state,
+                            cx,
+                        );
                         ((), element_state)
                     });
-                } else {
-                    self.element
-                        .paint(bounds, view_state, frame_state.as_mut().unwrap(), cx);
+                    None
                 }
-                ElementRenderPhase::Painted { frame_state }
             }
 
             _ => panic!("must call layout before paint"),
-        };
+        }
     }
 
     fn measure(
@@ -210,25 +204,25 @@ where
         view_state: &mut V,
         cx: &mut ViewContext<V>,
     ) -> Size<Pixels> {
-        if matches!(&self.phase, ElementRenderPhase::Start) {
+        if matches!(&self.phase, ElementDrawPhase::Start) {
             self.layout(view_state, cx);
         }
 
         let layout_id = match &mut self.phase {
-            ElementRenderPhase::LayoutRequested {
+            ElementDrawPhase::LayoutRequested {
                 layout_id,
                 frame_state,
             } => {
                 cx.compute_layout(*layout_id, available_space);
                 let layout_id = *layout_id;
-                self.phase = ElementRenderPhase::LayoutComputed {
+                self.phase = ElementDrawPhase::LayoutComputed {
                     layout_id,
                     available_space,
                     frame_state: frame_state.take(),
                 };
                 layout_id
             }
-            ElementRenderPhase::LayoutComputed {
+            ElementDrawPhase::LayoutComputed {
                 layout_id,
                 available_space: prev_available_space,
                 ..
@@ -246,17 +240,56 @@ where
     }
 
     fn draw(
-        &mut self,
+        mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
         view_state: &mut V,
         cx: &mut ViewContext<V>,
-    ) {
+    ) -> Option<E::State> {
         self.measure(available_space, view_state, cx);
         cx.with_absolute_element_offset(origin, |cx| self.paint(view_state, cx))
     }
 }
 
+impl<V, E> ElementObject<V> for Option<DrawableElement<V, E>>
+where
+    E: Element<V>,
+    E::State: 'static,
+{
+    fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
+        DrawableElement::layout(self.as_mut().unwrap(), view_state, cx)
+    }
+
+    fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
+        DrawableElement::paint(self.take().unwrap(), view_state, cx);
+    }
+
+    fn measure(
+        &mut self,
+        available_space: Size<AvailableSpace>,
+        view_state: &mut V,
+        cx: &mut ViewContext<V>,
+    ) -> Size<Pixels> {
+        DrawableElement::measure(self.as_mut().unwrap(), available_space, view_state, cx)
+    }
+
+    fn draw(
+        &mut self,
+        origin: Point<Pixels>,
+        available_space: Size<AvailableSpace>,
+        view_state: &mut V,
+        cx: &mut ViewContext<V>,
+    ) {
+        DrawableElement::draw(
+            self.take().unwrap(),
+            origin,
+            available_space,
+            view_state,
+            cx,
+        );
+    }
+}
+
 pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
 
 impl<V> AnyElement<V> {
@@ -264,16 +297,16 @@ impl<V> AnyElement<V> {
     where
         V: 'static,
         E: 'static + Element<V>,
-        E::ElementState: Any,
+        E::State: Any,
     {
-        AnyElement(Box::new(RenderedElement::new(element)))
+        AnyElement(Box::new(Some(DrawableElement::new(element))) as Box<dyn ElementObject<V>>)
     }
 
     pub fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId {
         self.0.layout(view_state, cx)
     }
 
-    pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
+    pub fn paint(mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
         self.0.paint(view_state, cx)
     }
 
@@ -289,7 +322,7 @@ impl<V> AnyElement<V> {
 
     /// Initializes this element and performs layout in the available space, then paints it at the given origin.
     pub fn draw(
-        &mut self,
+        mut self,
         origin: Point<Pixels>,
         available_space: Size<AvailableSpace>,
         view_state: &mut V,
@@ -343,7 +376,7 @@ where
     E: 'static + Component<V>,
     F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
 {
-    type ElementState = AnyElement<V>;
+    type State = Option<AnyElement<V>>;
 
     fn element_id(&self) -> Option<ElementId> {
         None
@@ -352,23 +385,23 @@ where
     fn layout(
         &mut self,
         view_state: &mut V,
-        _: Option<Self::ElementState>,
+        _: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let render = self.take().unwrap();
         let mut rendered_element = (render)(view_state, cx).render();
         let layout_id = rendered_element.layout(view_state, cx);
-        (layout_id, rendered_element)
+        (layout_id, Some(rendered_element))
     }
 
     fn paint(
-        &mut self,
+        self,
         _bounds: Bounds<Pixels>,
         view_state: &mut V,
-        rendered_element: &mut Self::ElementState,
+        rendered_element: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
-        rendered_element.paint(view_state, cx)
+        rendered_element.take().unwrap().paint(view_state, cx);
     }
 }
 

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

@@ -600,7 +600,7 @@ impl<V: 'static> ParentComponent<V> for Div<V> {
 }
 
 impl<V: 'static> Element<V> for Div<V> {
-    type ElementState = DivState;
+    type State = DivState;
 
     fn element_id(&self) -> Option<ElementId> {
         self.interactivity.element_id.clone()
@@ -609,9 +609,9 @@ impl<V: 'static> Element<V> for Div<V> {
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let mut child_layout_ids = SmallVec::new();
         let mut interactivity = mem::take(&mut self.interactivity);
         let (layout_id, interactive_state) = interactivity.layout(
@@ -639,10 +639,10 @@ impl<V: 'static> Element<V> for Div<V> {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         let mut child_min = point(Pixels::MAX, Pixels::MAX);
@@ -658,8 +658,7 @@ impl<V: 'static> Element<V> for Div<V> {
             (child_max - child_min).into()
         };
 
-        let mut interactivity = mem::take(&mut self.interactivity);
-        interactivity.paint(
+        self.interactivity.paint(
             bounds,
             content_size,
             &mut element_state.interactive_state,
@@ -679,7 +678,7 @@ impl<V: 'static> Element<V> for Div<V> {
                         cx.with_text_style(style.text_style().cloned(), |cx| {
                             cx.with_content_mask(style.overflow_mask(bounds), |cx| {
                                 cx.with_element_offset(scroll_offset, |cx| {
-                                    for child in &mut self.children {
+                                    for child in self.children {
                                         child.paint(view_state, cx);
                                     }
                                 })
@@ -689,7 +688,6 @@ impl<V: 'static> Element<V> for Div<V> {
                 })
             },
         );
-        self.interactivity = interactivity;
     }
 }
 
@@ -770,7 +768,7 @@ where
     }
 
     pub fn paint(
-        &mut self,
+        mut self,
         bounds: Bounds<Pixels>,
         content_size: Size<Pixels>,
         element_state: &mut InteractiveElementState,
@@ -786,25 +784,25 @@ where
             }
         }
 
-        for listener in self.mouse_down_listeners.drain(..) {
+        for listener in self.mouse_down_listeners {
             cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.mouse_up_listeners.drain(..) {
+        for listener in self.mouse_up_listeners {
             cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.mouse_move_listeners.drain(..) {
+        for listener in self.mouse_move_listeners {
             cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
         }
 
-        for listener in self.scroll_wheel_listeners.drain(..) {
+        for listener in self.scroll_wheel_listeners {
             cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| {
                 listener(state, event, &bounds, phase, cx);
             })
@@ -840,7 +838,7 @@ where
         }
 
         if cx.active_drag.is_some() {
-            let drop_listeners = mem::take(&mut self.drop_listeners);
+            let drop_listeners = self.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) =
@@ -1062,24 +1060,24 @@ where
             self.key_context.clone(),
             element_state.focus_handle.clone(),
             |_, cx| {
-                for listener in self.key_down_listeners.drain(..) {
+                for listener in self.key_down_listeners {
                     cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
                         listener(state, event, phase, cx);
                     })
                 }
 
-                for listener in self.key_up_listeners.drain(..) {
+                for listener in self.key_up_listeners {
                     cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
                         listener(state, event, phase, cx);
                     })
                 }
 
-                for (action_type, listener) in self.action_listeners.drain(..) {
+                for (action_type, listener) in self.action_listeners {
                     cx.on_action(action_type, listener)
                 }
 
                 if let Some(focus_handle) = element_state.focus_handle.as_ref() {
-                    for listener in self.focus_listeners.drain(..) {
+                    for listener in self.focus_listeners {
                         let focus_handle = focus_handle.clone();
                         cx.on_focus_changed(move |view, event, cx| {
                             listener(view, &focus_handle, event, cx)
@@ -1291,7 +1289,7 @@ where
     V: 'static,
     E: Element<V>,
 {
-    type ElementState = E::ElementState;
+    type State = E::State;
 
     fn element_id(&self) -> Option<ElementId> {
         self.element.element_id()
@@ -1300,20 +1298,20 @@ where
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         self.element.layout(view_state, element_state, cx)
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
-        self.element.paint(bounds, view_state, element_state, cx);
+        self.element.paint(bounds, view_state, element_state, cx)
     }
 }
 
@@ -1377,7 +1375,7 @@ where
     V: 'static,
     E: Element<V>,
 {
-    type ElementState = E::ElementState;
+    type State = E::State;
 
     fn element_id(&self) -> Option<ElementId> {
         self.element.element_id()
@@ -1386,17 +1384,17 @@ where
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         self.element.layout(view_state, element_state, cx)
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         self.element.paint(bounds, view_state, element_state, cx)

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

@@ -42,7 +42,7 @@ impl<V> Component<V> for Img<V> {
 }
 
 impl<V> Element<V> for Img<V> {
-    type ElementState = InteractiveElementState;
+    type State = InteractiveElementState;
 
     fn element_id(&self) -> Option<crate::ElementId> {
         self.interactivity.element_id.clone()
@@ -51,19 +51,19 @@ impl<V> Element<V> for Img<V> {
     fn layout(
         &mut self,
         _view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         self.interactivity.layout(element_state, cx, |style, cx| {
             cx.request_layout(&style, None)
         })
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         _view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         self.interactivity.paint(

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

@@ -64,7 +64,7 @@ impl<V: 'static> Component<V> for Overlay<V> {
 }
 
 impl<V: 'static> Element<V> for Overlay<V> {
-    type ElementState = OverlayState;
+    type State = OverlayState;
 
     fn element_id(&self) -> Option<crate::ElementId> {
         None
@@ -73,9 +73,9 @@ impl<V: 'static> Element<V> for Overlay<V> {
     fn layout(
         &mut self,
         view_state: &mut V,
-        _: Option<Self::ElementState>,
+        _: Option<Self::State>,
         cx: &mut crate::ViewContext<V>,
-    ) -> (crate::LayoutId, Self::ElementState) {
+    ) -> (crate::LayoutId, Self::State) {
         let child_layout_ids = self
             .children
             .iter_mut()
@@ -92,10 +92,10 @@ impl<V: 'static> Element<V> for Overlay<V> {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: crate::Bounds<crate::Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut crate::ViewContext<V>,
     ) {
         if element_state.child_layout_ids.is_empty() {
@@ -156,7 +156,7 @@ impl<V: 'static> Element<V> for Overlay<V> {
         }
 
         cx.with_element_offset(desired.origin - bounds.origin, |cx| {
-            for child in &mut self.children {
+            for child in self.children {
                 child.paint(view_state, cx);
             }
         })

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

@@ -31,7 +31,7 @@ impl<V> Component<V> for Svg<V> {
 }
 
 impl<V> Element<V> for Svg<V> {
-    type ElementState = InteractiveElementState;
+    type State = InteractiveElementState;
 
     fn element_id(&self) -> Option<ElementId> {
         self.interactivity.element_id.clone()
@@ -40,19 +40,19 @@ impl<V> Element<V> for Svg<V> {
     fn layout(
         &mut self,
         _view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         self.interactivity.layout(element_state, cx, |style, cx| {
             cx.request_layout(&style, None)
         })
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         _view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) where
         Self: Sized,

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

@@ -9,7 +9,7 @@ use std::{cell::Cell, rc::Rc, sync::Arc};
 use util::ResultExt;
 
 impl<V: 'static> Element<V> for &'static str {
-    type ElementState = TextState;
+    type State = TextState;
 
     fn element_id(&self) -> Option<ElementId> {
         None
@@ -18,16 +18,16 @@ impl<V: 'static> Element<V> for &'static str {
     fn layout(
         &mut self,
         _: &mut V,
-        _: Option<Self::ElementState>,
+        _: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(SharedString::from(*self), None, cx);
         (layout_id, state)
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         _: &mut V,
         state: &mut TextState,
@@ -38,7 +38,7 @@ impl<V: 'static> Element<V> for &'static str {
 }
 
 impl<V: 'static> Element<V> for SharedString {
-    type ElementState = TextState;
+    type State = TextState;
 
     fn element_id(&self) -> Option<ElementId> {
         Some(self.clone().into())
@@ -47,16 +47,16 @@ impl<V: 'static> Element<V> for SharedString {
     fn layout(
         &mut self,
         _: &mut V,
-        _: Option<Self::ElementState>,
+        _: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let mut state = TextState::default();
         let layout_id = state.layout(self.clone(), None, cx);
         (layout_id, state)
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         _: &mut V,
         state: &mut TextState,
@@ -93,7 +93,7 @@ impl<V: 'static> Component<V> for Text {
 }
 
 impl<V: 'static> Element<V> for Text {
-    type ElementState = TextState;
+    type State = TextState;
 
     fn element_id(&self) -> Option<crate::ElementId> {
         None
@@ -102,9 +102,9 @@ impl<V: 'static> Element<V> for Text {
     fn layout(
         &mut self,
         _view: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let element_state = element_state.unwrap_or_default();
         let text_system = cx.text_system().clone();
         let text_style = cx.text_style();
@@ -160,10 +160,10 @@ impl<V: 'static> Element<V> for Text {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         _: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         let element_state = element_state.lock();
@@ -280,7 +280,7 @@ struct InteractiveTextState {
 }
 
 impl<V: 'static> Element<V> for InteractiveText {
-    type ElementState = InteractiveTextState;
+    type State = InteractiveTextState;
 
     fn element_id(&self) -> Option<ElementId> {
         Some(self.id.clone())
@@ -289,9 +289,9 @@ impl<V: 'static> Element<V> for InteractiveText {
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         if let Some(InteractiveTextState {
             text_state,
             clicked_range_ixs,
@@ -314,10 +314,10 @@ impl<V: 'static> Element<V> for InteractiveText {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         self.text

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

@@ -4,7 +4,7 @@ use crate::{
     Point, Size, StyleRefinement, Styled, ViewContext,
 };
 use smallvec::SmallVec;
-use std::{cell::RefCell, cmp, mem, ops::Range, rc::Rc};
+use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
 use taffy::style::Overflow;
 
 /// uniform_list provides lazy rendering for a set of items that are of uniform height.
@@ -102,7 +102,7 @@ pub struct UniformListState {
 }
 
 impl<V: 'static> Element<V> for UniformList<V> {
-    type ElementState = UniformListState;
+    type State = UniformListState;
 
     fn element_id(&self) -> Option<crate::ElementId> {
         Some(self.id.clone())
@@ -111,9 +111,9 @@ impl<V: 'static> Element<V> for UniformList<V> {
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut ViewContext<V>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         let max_items = self.item_count;
         let rem_size = cx.rem_size();
         let item_size = element_state
@@ -159,10 +159,10 @@ impl<V: 'static> Element<V> for UniformList<V> {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<crate::Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut ViewContext<V>,
     ) {
         let style =
@@ -183,14 +183,17 @@ impl<V: 'static> Element<V> for UniformList<V> {
             height: item_size.height * self.item_count,
         };
 
-        let mut interactivity = mem::take(&mut self.interactivity);
         let shared_scroll_offset = element_state
             .interactive
             .scroll_offset
             .get_or_insert_with(Rc::default)
             .clone();
 
-        interactivity.paint(
+        let item_height = self
+            .measure_item(view_state, Some(padded_bounds.size.width), cx)
+            .height;
+
+        self.interactivity.paint(
             bounds,
             content_size,
             &mut element_state.interactive,
@@ -209,9 +212,6 @@ impl<V: 'static> Element<V> for UniformList<V> {
                     style.paint(bounds, cx);
 
                     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.borrow_mut().replace(ScrollHandleState {
                                 item_height,
@@ -233,9 +233,9 @@ impl<V: 'static> Element<V> for UniformList<V> {
                                 self.item_count,
                             );
 
-                        let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
+                        let 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) {
+                            for (item, ix) in items.into_iter().zip(visible_range) {
                                 let item_origin = padded_bounds.origin
                                     + point(px(0.), item_height * ix + scroll_offset.y);
                                 let available_space = size(
@@ -249,7 +249,6 @@ impl<V: 'static> Element<V> for UniformList<V> {
                 })
             },
         );
-        self.interactivity = interactivity;
     }
 }
 

crates/gpui2/src/gpui2.rs 🔗

@@ -78,8 +78,6 @@ use std::{
 };
 use taffy::TaffyLayoutEngine;
 
-type AnyBox = Box<dyn Any>;
-
 pub trait Context {
     type Result<T>;
 

crates/gpui2/src/view.rs 🔗

@@ -1,8 +1,7 @@
 use crate::{
-    private::Sealed, AnyBox, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace,
-    BorrowWindow, Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, FocusHandle,
-    FocusableView, LayoutId, Model, Pixels, Point, Size, ViewContext, VisualContext, WeakModel,
-    WindowContext,
+    private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
+    Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
+    LayoutId, Model, Pixels, Point, Size, ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
 use std::{
@@ -164,7 +163,7 @@ impl<V> Eq for WeakView<V> {}
 pub struct AnyView {
     model: AnyModel,
     layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
-    paint: fn(&AnyView, &mut AnyBox, &mut WindowContext),
+    paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
 }
 
 impl AnyView {
@@ -198,11 +197,11 @@ impl AnyView {
         cx: &mut WindowContext,
     ) {
         cx.with_absolute_element_offset(origin, |cx| {
-            let (layout_id, mut rendered_element) = (self.layout)(self, cx);
+            let (layout_id, rendered_element) = (self.layout)(self, cx);
             cx.window
                 .layout_engine
                 .compute_layout(layout_id, available_space);
-            (self.paint)(self, &mut rendered_element, cx);
+            (self.paint)(self, rendered_element, cx);
         })
     }
 }
@@ -224,7 +223,7 @@ impl<V: Render> From<View<V>> for AnyView {
 }
 
 impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
-    type ElementState = Box<dyn Any>;
+    type State = Option<Box<dyn Any>>;
 
     fn element_id(&self) -> Option<ElementId> {
         Some(self.model.entity_id.into())
@@ -233,27 +232,28 @@ impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
     fn layout(
         &mut self,
         _view_state: &mut ParentViewState,
-        _element_state: Option<Self::ElementState>,
+        _element_state: Option<Self::State>,
         cx: &mut ViewContext<ParentViewState>,
-    ) -> (LayoutId, Self::ElementState) {
-        (self.layout)(self, cx)
+    ) -> (LayoutId, Self::State) {
+        let (layout_id, rendered_element) = (self.layout)(self, cx);
+        (layout_id, Some(rendered_element))
     }
 
     fn paint(
-        &mut self,
+        mut self,
         _bounds: Bounds<Pixels>,
         _view_state: &mut ParentViewState,
-        rendered_element: &mut Self::ElementState,
+        rendered_element: &mut Self::State,
         cx: &mut ViewContext<ParentViewState>,
     ) {
-        (self.paint)(self, rendered_element, cx)
+        (self.paint)(&mut self, rendered_element.take().unwrap(), cx)
     }
 }
 
 pub struct AnyWeakView {
     model: AnyWeakModel,
     layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
-    paint: fn(&AnyView, &mut AnyBox, &mut WindowContext),
+    paint: fn(&AnyView, Box<dyn Any>, &mut WindowContext),
 }
 
 impl AnyWeakView {
@@ -311,7 +311,7 @@ where
     ParentViewState: 'static,
     ViewState: 'static,
 {
-    type ElementState = AnyElement<ViewState>;
+    type State = Option<AnyElement<ViewState>>;
 
     fn element_id(&self) -> Option<ElementId> {
         Some(self.view.entity_id().into())
@@ -320,24 +320,25 @@ where
     fn layout(
         &mut self,
         _: &mut ParentViewState,
-        _: Option<Self::ElementState>,
+        _: Option<Self::State>,
         cx: &mut ViewContext<ParentViewState>,
-    ) -> (LayoutId, Self::ElementState) {
+    ) -> (LayoutId, Self::State) {
         self.view.update(cx, |view, cx| {
             let mut element = self.component.take().unwrap().render();
             let layout_id = element.layout(view, cx);
-            (layout_id, element)
+            (layout_id, Some(element))
         })
     }
 
     fn paint(
-        &mut self,
+        self,
         _: Bounds<Pixels>,
         _: &mut ParentViewState,
-        element: &mut Self::ElementState,
+        element: &mut Self::State,
         cx: &mut ViewContext<ParentViewState>,
     ) {
-        self.view.update(cx, |view, cx| element.paint(view, cx))
+        self.view
+            .update(cx, |view, cx| element.take().unwrap().paint(view, cx))
     }
 }
 
@@ -359,14 +360,10 @@ mod any_view {
         })
     }
 
-    pub(crate) fn paint<V: Render>(
-        view: &AnyView,
-        element: &mut Box<dyn Any>,
-        cx: &mut WindowContext,
-    ) {
+    pub(crate) fn paint<V: Render>(view: &AnyView, element: Box<dyn Any>, cx: &mut WindowContext) {
         cx.with_element_id(Some(view.model.entity_id), |cx| {
             let view = view.clone().downcast::<V>().unwrap();
-            let element = element.downcast_mut::<AnyElement<V>>().unwrap();
+            let element = element.downcast::<AnyElement<V>>().unwrap();
             view.update(cx, |view, cx| element.paint(view, cx))
         })
     }

crates/gpui2/src/window.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{
-    key_dispatch::DispatchActionListener, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext,
+    key_dispatch::DispatchActionListener, px, size, Action, AnyDrag, AnyView, AppContext,
     AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
     DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
     EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
@@ -237,7 +237,7 @@ pub struct Window {
 
 // #[derive(Default)]
 pub(crate) struct Frame {
-    pub(crate) element_states: HashMap<GlobalElementId, AnyBox>,
+    pub(crate) element_states: HashMap<GlobalElementId, Box<dyn Any>>,
     mouse_listeners: HashMap<TypeId, Vec<(StackingOrder, AnyMouseListener)>>,
     pub(crate) dispatch_tree: DispatchTree,
     pub(crate) focus_listeners: Vec<AnyFocusListener>,

crates/gpui2_macros/src/gpui2_macros.rs 🔗

@@ -1,5 +1,6 @@
 mod action;
 mod derive_component;
+mod derive_element;
 mod register_action;
 mod style_helpers;
 mod test;
@@ -26,6 +27,11 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
     derive_component::derive_component(input)
 }
 
+// #[proc_macro_derive(Element)]
+// pub fn derive_element(input: TokenStream) -> TokenStream {
+//     derive_element::derive_element(input)
+// }
+
 #[proc_macro_attribute]
 pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
     test::test(args, function)

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

@@ -133,7 +133,7 @@ pub struct MenuHandleState<V, M> {
     menu_element: Option<AnyElement<V>>,
 }
 impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
-    type ElementState = MenuHandleState<V, M>;
+    type State = MenuHandleState<V, M>;
 
     fn element_id(&self) -> Option<gpui::ElementId> {
         Some(self.id.clone().expect("menu_handle must have an id()"))
@@ -142,9 +142,9 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
     fn layout(
         &mut self,
         view_state: &mut V,
-        element_state: Option<Self::ElementState>,
+        element_state: Option<Self::State>,
         cx: &mut crate::ViewContext<V>,
-    ) -> (gpui::LayoutId, Self::ElementState) {
+    ) -> (gpui::LayoutId, Self::State) {
         let (menu, position) = if let Some(element_state) = element_state {
             (element_state.menu, element_state.position)
         } else {
@@ -192,22 +192,22 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
     }
 
     fn paint(
-        &mut self,
+        self,
         bounds: Bounds<gpui::Pixels>,
         view_state: &mut V,
-        element_state: &mut Self::ElementState,
+        element_state: &mut Self::State,
         cx: &mut crate::ViewContext<V>,
     ) {
-        if let Some(child) = element_state.child_element.as_mut() {
+        if let Some(child) = element_state.child_element.take() {
             child.paint(view_state, cx);
         }
 
-        if let Some(menu) = element_state.menu_element.as_mut() {
+        if let Some(menu) = element_state.menu_element.take() {
             menu.paint(view_state, cx);
             return;
         }
 
-        let Some(builder) = self.menu_builder.clone() else {
+        let Some(builder) = self.menu_builder else {
             return;
         };
         let menu = element_state.menu.clone();