WIP: Start on List::layout

Nathan Sobo created

Perform layout on any elements that we haven't yet laid out.

Change summary

gpui/src/elements/list.rs | 101 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 4 deletions(-)

Detailed changes

gpui/src/elements/list.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     geometry::{rect::RectF, vector::Vector2F},
-    sum_tree::{self, SumTree},
+    sum_tree::{self, Bias, SumTree},
     Element,
 };
 use parking_lot::Mutex;
@@ -15,6 +15,7 @@ pub struct List {
 pub struct ListState(Arc<Mutex<StateInner>>);
 
 struct StateInner {
+    last_layout_width: f32,
     elements: Vec<ElementBox>,
     heights: SumTree<ElementHeight>,
 }
@@ -27,10 +28,20 @@ enum ElementHeight {
 
 #[derive(Clone, Debug, Default)]
 struct ElementHeightSummary {
+    count: usize,
     pending_count: usize,
     height: f32,
 }
 
+#[derive(Clone, Debug, Default)]
+struct Count(usize);
+
+#[derive(Clone, Debug, Default)]
+struct PendingCount(usize);
+
+#[derive(Clone, Debug, Default)]
+struct Height(f32);
+
 impl Element for List {
     type LayoutState = ();
 
@@ -41,6 +52,37 @@ impl Element for List {
         constraint: crate::SizeConstraint,
         cx: &mut crate::LayoutContext,
     ) -> (Vector2F, Self::LayoutState) {
+        // TODO: Fully invalidate if width has changed since the last layout.
+
+        let state = &mut *self.state.0.lock();
+        let mut old_heights = state.heights.cursor::<PendingCount, ElementHeightSummary>();
+        let mut new_heights = old_heights.slice(&PendingCount(1), sum_tree::Bias::Left, &());
+
+        let mut item_constraint = constraint;
+        item_constraint.min.set_y(0.);
+        item_constraint.max.set_y(f32::INFINITY);
+
+        while let Some(height) = old_heights.item() {
+            if height.is_pending() {
+                let size =
+                    state.elements[old_heights.sum_start().count].layout(item_constraint, cx);
+                new_heights.push(ElementHeight::Ready(size.y()), &());
+                old_heights.next(&());
+            } else {
+                new_heights.push_tree(
+                    old_heights.slice(
+                        &PendingCount(old_heights.sum_start().pending_count + 1),
+                        Bias::Left,
+                        &(),
+                    ),
+                    &(),
+                );
+            }
+        }
+
+        drop(old_heights);
+        state.heights = new_heights;
+
         todo!()
     }
 
@@ -79,7 +121,17 @@ impl ListState {
     pub fn new(elements: Vec<ElementBox>) -> Self {
         let mut heights = SumTree::new();
         heights.extend(elements.iter().map(|_| ElementHeight::Pending), &());
-        Self(Arc::new(Mutex::new(StateInner { elements, heights })))
+        Self(Arc::new(Mutex::new(StateInner {
+            last_layout_width: 0.,
+            elements,
+            heights,
+        })))
+    }
+}
+
+impl ElementHeight {
+    fn is_pending(&self) -> bool {
+        matches!(self, ElementHeight::Pending)
     }
 }
 
@@ -87,15 +139,56 @@ impl sum_tree::Item for ElementHeight {
     type Summary = ElementHeightSummary;
 
     fn summary(&self) -> Self::Summary {
-        todo!()
+        match self {
+            ElementHeight::Pending => ElementHeightSummary {
+                count: 1,
+                pending_count: 1,
+                height: 0.,
+            },
+            ElementHeight::Ready(height) => ElementHeightSummary {
+                count: 1,
+                pending_count: 0,
+                height: *height,
+            },
+        }
     }
 }
 
 impl sum_tree::Summary for ElementHeightSummary {
     type Context = ();
 
-    fn add_summary(&mut self, summary: &Self, cx: &Self::Context) {
+    fn add_summary(&mut self, summary: &Self, _: &()) {
         self.pending_count += summary.pending_count;
         self.height += summary.height;
     }
 }
+
+impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for ElementHeightSummary {
+    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
+        sum_tree::Summary::add_summary(self, summary, &());
+    }
+}
+
+impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Count {
+    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
+        self.0 += summary.count;
+    }
+}
+
+impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for PendingCount {
+    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
+        self.0 += summary.pending_count;
+    }
+}
+
+impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for PendingCount {
+    fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering {
+        self.0.cmp(&other.0)
+    }
+}
+
+impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Height {
+    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
+        self.0 += summary.height;
+    }
+}