Extract `AnyElement::{measure,draw}`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/gpui2/src/element.rs               | 31 +++++++++++++++++++
crates/gpui2/src/elements/uniform_list.rs | 38 +++++++++---------------
2 files changed, 44 insertions(+), 25 deletions(-)

Detailed changes

crates/gpui2/src/element.rs 🔗

@@ -1,4 +1,6 @@
-use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, ViewContext};
+use crate::{
+    AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, ViewContext,
+};
 use derive_more::{Deref, DerefMut};
 pub(crate) use smallvec::SmallVec;
 use std::{any::Any, mem};
@@ -196,6 +198,33 @@ impl<V> AnyElement<V> {
     pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
         self.0.paint(view_state, cx)
     }
+
+    /// Initializes this element and performs layout within the given available space to determine its size.
+    pub fn measure(
+        &mut self,
+        available_space: Size<AvailableSpace>,
+        view_state: &mut V,
+        cx: &mut ViewContext<V>,
+    ) -> Size<Pixels> {
+        self.initialize(view_state, cx);
+        let layout_id = self.layout(view_state, cx);
+        cx.compute_layout(layout_id, available_space);
+        cx.layout_bounds(layout_id).size
+    }
+
+    /// Initializes this element and performs layout in the available space, then paints it at the given origin.
+    pub fn draw(
+        &mut self,
+        origin: Point<Pixels>,
+        available_space: Size<AvailableSpace>,
+        view_state: &mut V,
+        cx: &mut ViewContext<V>,
+    ) {
+        self.initialize(view_state, cx);
+        let layout_id = self.layout(view_state, cx);
+        cx.compute_layout(layout_id, available_space);
+        cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
+    }
 }
 
 pub trait Component<V> {

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

@@ -1,6 +1,6 @@
 use crate::{
-    point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
-    ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
+    point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
+    ElementId, ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
     StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
     StyleRefinement, Styled, ViewContext,
 };
@@ -165,19 +165,13 @@ impl<V: 'static> Element<V> for UniformList<V> {
 
                 cx.with_z_index(1, |cx| {
                     for (item, ix) in items.iter_mut().zip(visible_range) {
-                        item.initialize(view_state, cx);
-
-                        let layout_id = item.layout(view_state, cx);
-                        cx.compute_layout(
-                            layout_id,
-                            Size {
-                                width: AvailableSpace::Definite(bounds.size.width),
-                                height: AvailableSpace::Definite(item_height),
-                            },
-                        );
-                        let offset =
+                        let item_origin =
                             padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
-                        cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
+                        let available_space = size(
+                            AvailableSpace::Definite(bounds.size.width),
+                            AvailableSpace::Definite(item_height),
+                        );
+                        item.draw(item_origin, available_space, view_state, cx);
                     }
                 });
             } else {
@@ -205,18 +199,14 @@ impl<V> UniformList<V> {
         cx: &mut ViewContext<V>,
     ) -> Pixels {
         let mut items = (self.render_items)(view_state, 0..1, cx);
-        debug_assert!(items.len() == 1);
+        debug_assert_eq!(items.len(), 1);
         let mut item_to_measure = items.pop().unwrap();
-        item_to_measure.initialize(view_state, cx);
-        let layout_id = item_to_measure.layout(view_state, cx);
-        cx.compute_layout(
-            layout_id,
-            Size {
-                width: AvailableSpace::Definite(list_bounds.size.width),
-                height: AvailableSpace::MinContent,
-            },
+        let available_space = size(
+            AvailableSpace::Definite(list_bounds.size.width),
+            AvailableSpace::MinContent,
         );
-        cx.layout_bounds(layout_id).size.height
+        let size = item_to_measure.measure(available_space, view_state, cx);
+        size.height
     }
 
     pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {