diff --git a/gpui/src/elements/list.rs b/gpui/src/elements/list.rs index d64b10cf4c..ecf407946b 100644 --- a/gpui/src/elements/list.rs +++ b/gpui/src/elements/list.rs @@ -4,7 +4,7 @@ use crate::{ Element, }; use parking_lot::Mutex; -use std::sync::Arc; +use std::{ops::Range, sync::Arc}; use crate::ElementBox; @@ -12,6 +12,7 @@ pub struct List { state: ListState, } +#[derive(Clone)] pub struct ListState(Arc>); struct StateInner { @@ -26,7 +27,7 @@ enum ElementHeight { Ready(f32), } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq)] struct ElementHeightSummary { count: usize, pending_count: usize, @@ -42,6 +43,12 @@ struct PendingCount(usize); #[derive(Clone, Debug, Default)] struct Height(f32); +impl List { + pub fn new(state: ListState) -> Self { + Self { state } + } +} + impl Element for List { type LayoutState = (); @@ -82,8 +89,7 @@ impl Element for List { drop(old_heights); state.heights = new_heights; - - todo!() + (constraint.max, ()) } fn paint( @@ -127,6 +133,33 @@ impl ListState { heights, }))) } + + pub fn splice( + &self, + old_range: Range, + new_elements: impl IntoIterator, + ) { + let state = &mut *self.0.lock(); + + let mut old_heights = state.heights.cursor::(); + let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &()); + old_heights.seek_forward(&Count(old_range.end), Bias::Right, &()); + + let mut len = 0; + let old_elements = state.elements.splice( + old_range, + new_elements.into_iter().map(|e| { + len += 1; + e + }), + ); + drop(old_elements); + + new_heights.extend((0..len).map(|_| ElementHeight::Pending), &()); + new_heights.push_tree(old_heights.suffix(&()), &()); + drop(old_heights); + state.heights = new_heights; + } } impl ElementHeight { @@ -158,6 +191,7 @@ impl sum_tree::Summary for ElementHeightSummary { type Context = (); fn add_summary(&mut self, summary: &Self, _: &()) { + self.count += summary.count; self.pending_count += summary.pending_count; self.height += summary.height; } @@ -175,6 +209,12 @@ impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Count { } } +impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for Count { + fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for PendingCount { fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) { self.0 += summary.pending_count; @@ -196,10 +236,58 @@ impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Height { #[cfg(test)] mod tests { use super::*; + use crate::{elements::*, geometry::vector::vec2f}; #[crate::test(self)] fn test_layout(cx: &mut crate::MutableAppContext) { let mut presenter = cx.build_presenter(0, 20.0); - let layout_cx = presenter.layout_cx(cx); + let mut layout_cx = presenter.layout_cx(cx); + let state = ListState::new(vec![item(20.), item(30.), item(10.)]); + let mut list = List::new(state.clone()).boxed(); + + let size = list.layout( + SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)), + &mut layout_cx, + ); + assert_eq!(size, vec2f(100., 40.)); + assert_eq!( + state.0.lock().heights.summary(), + ElementHeightSummary { + count: 3, + pending_count: 0, + height: 60. + } + ); + + state.splice(1..2, vec![item(40.), item(50.)]); + state.splice(3..3, vec![item(60.)]); + assert_eq!( + state.0.lock().heights.summary(), + ElementHeightSummary { + count: 5, + pending_count: 3, + height: 30. + } + ); + let size = list.layout( + SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)), + &mut layout_cx, + ); + assert_eq!(size, vec2f(100., 40.)); + assert_eq!( + state.0.lock().heights.summary(), + ElementHeightSummary { + count: 5, + pending_count: 0, + height: 180. + } + ); + } + + fn item(height: f32) -> ElementBox { + ConstrainedBox::new(Empty::new().boxed()) + .with_height(height) + .with_width(100.) + .boxed() } }