list.rs

  1use crate::{
  2    geometry::{rect::RectF, vector::Vector2F},
  3    sum_tree::{self, Bias, SumTree},
  4    Element,
  5};
  6use parking_lot::Mutex;
  7use std::{ops::Range, sync::Arc};
  8
  9use crate::ElementBox;
 10
 11pub struct List {
 12    state: ListState,
 13}
 14
 15#[derive(Clone)]
 16pub struct ListState(Arc<Mutex<StateInner>>);
 17
 18struct StateInner {
 19    last_layout_width: f32,
 20    elements: Vec<ElementBox>,
 21    heights: SumTree<ElementHeight>,
 22}
 23
 24#[derive(Clone, Debug)]
 25enum ElementHeight {
 26    Pending,
 27    Ready(f32),
 28}
 29
 30#[derive(Clone, Debug, Default, PartialEq)]
 31struct ElementHeightSummary {
 32    count: usize,
 33    pending_count: usize,
 34    height: f32,
 35}
 36
 37#[derive(Clone, Debug, Default)]
 38struct Count(usize);
 39
 40#[derive(Clone, Debug, Default)]
 41struct PendingCount(usize);
 42
 43#[derive(Clone, Debug, Default)]
 44struct Height(f32);
 45
 46impl List {
 47    pub fn new(state: ListState) -> Self {
 48        Self { state }
 49    }
 50}
 51
 52impl Element for List {
 53    type LayoutState = ();
 54
 55    type PaintState = ();
 56
 57    fn layout(
 58        &mut self,
 59        constraint: crate::SizeConstraint,
 60        cx: &mut crate::LayoutContext,
 61    ) -> (Vector2F, Self::LayoutState) {
 62        let state = &mut *self.state.0.lock();
 63        let mut item_constraint = constraint;
 64        item_constraint.min.set_y(0.);
 65        item_constraint.max.set_y(f32::INFINITY);
 66
 67        if state.last_layout_width == constraint.max.x() {
 68            let mut old_heights = state.heights.cursor::<PendingCount, ElementHeightSummary>();
 69            let mut new_heights = old_heights.slice(&PendingCount(1), sum_tree::Bias::Left, &());
 70
 71            while let Some(height) = old_heights.item() {
 72                if height.is_pending() {
 73                    let size =
 74                        state.elements[old_heights.sum_start().count].layout(item_constraint, cx);
 75                    new_heights.push(ElementHeight::Ready(size.y()), &());
 76                    old_heights.next(&());
 77                } else {
 78                    new_heights.push_tree(
 79                        old_heights.slice(
 80                            &PendingCount(old_heights.sum_start().pending_count + 1),
 81                            Bias::Left,
 82                            &(),
 83                        ),
 84                        &(),
 85                    );
 86                }
 87            }
 88
 89            drop(old_heights);
 90            state.heights = new_heights;
 91        } else {
 92            state.heights = SumTree::new();
 93            for element in &mut state.elements {
 94                let size = element.layout(item_constraint, cx);
 95                state.heights.push(ElementHeight::Ready(size.y()), &());
 96            }
 97            state.last_layout_width = constraint.max.x();
 98        }
 99
100        (constraint.max, ())
101    }
102
103    fn paint(
104        &mut self,
105        bounds: RectF,
106        layout: &mut Self::LayoutState,
107        cx: &mut crate::PaintContext,
108    ) -> Self::PaintState {
109        todo!()
110    }
111
112    fn dispatch_event(
113        &mut self,
114        event: &crate::Event,
115        bounds: RectF,
116        layout: &mut Self::LayoutState,
117        paint: &mut Self::PaintState,
118        cx: &mut crate::EventContext,
119    ) -> bool {
120        todo!()
121    }
122
123    fn debug(
124        &self,
125        bounds: RectF,
126        layout: &Self::LayoutState,
127        paint: &Self::PaintState,
128        cx: &crate::DebugContext,
129    ) -> serde_json::Value {
130        todo!()
131    }
132}
133
134impl ListState {
135    pub fn new(elements: Vec<ElementBox>) -> Self {
136        let mut heights = SumTree::new();
137        heights.extend(elements.iter().map(|_| ElementHeight::Pending), &());
138        Self(Arc::new(Mutex::new(StateInner {
139            last_layout_width: 0.,
140            elements,
141            heights,
142        })))
143    }
144
145    pub fn splice(
146        &self,
147        old_range: Range<usize>,
148        new_elements: impl IntoIterator<Item = ElementBox>,
149    ) {
150        let state = &mut *self.0.lock();
151
152        let mut old_heights = state.heights.cursor::<Count, ()>();
153        let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
154        old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
155
156        let mut len = 0;
157        let old_elements = state.elements.splice(
158            old_range,
159            new_elements.into_iter().map(|e| {
160                len += 1;
161                e
162            }),
163        );
164        drop(old_elements);
165
166        new_heights.extend((0..len).map(|_| ElementHeight::Pending), &());
167        new_heights.push_tree(old_heights.suffix(&()), &());
168        drop(old_heights);
169        state.heights = new_heights;
170    }
171}
172
173impl ElementHeight {
174    fn is_pending(&self) -> bool {
175        matches!(self, ElementHeight::Pending)
176    }
177}
178
179impl sum_tree::Item for ElementHeight {
180    type Summary = ElementHeightSummary;
181
182    fn summary(&self) -> Self::Summary {
183        match self {
184            ElementHeight::Pending => ElementHeightSummary {
185                count: 1,
186                pending_count: 1,
187                height: 0.,
188            },
189            ElementHeight::Ready(height) => ElementHeightSummary {
190                count: 1,
191                pending_count: 0,
192                height: *height,
193            },
194        }
195    }
196}
197
198impl sum_tree::Summary for ElementHeightSummary {
199    type Context = ();
200
201    fn add_summary(&mut self, summary: &Self, _: &()) {
202        self.count += summary.count;
203        self.pending_count += summary.pending_count;
204        self.height += summary.height;
205    }
206}
207
208impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for ElementHeightSummary {
209    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
210        sum_tree::Summary::add_summary(self, summary, &());
211    }
212}
213
214impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Count {
215    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
216        self.0 += summary.count;
217    }
218}
219
220impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for Count {
221    fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering {
222        self.0.cmp(&other.0)
223    }
224}
225
226impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for PendingCount {
227    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
228        self.0 += summary.pending_count;
229    }
230}
231
232impl<'a> sum_tree::SeekDimension<'a, ElementHeightSummary> for PendingCount {
233    fn cmp(&self, other: &Self, _: &()) -> std::cmp::Ordering {
234        self.0.cmp(&other.0)
235    }
236}
237
238impl<'a> sum_tree::Dimension<'a, ElementHeightSummary> for Height {
239    fn add_summary(&mut self, summary: &'a ElementHeightSummary, _: &()) {
240        self.0 += summary.height;
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247    use crate::{elements::*, geometry::vector::vec2f};
248
249    #[crate::test(self)]
250    fn test_layout(cx: &mut crate::MutableAppContext) {
251        let mut presenter = cx.build_presenter(0, 20.0);
252        let mut layout_cx = presenter.layout_cx(cx);
253        let state = ListState::new(vec![item(20.), item(30.), item(10.)]);
254        let mut list = List::new(state.clone()).boxed();
255
256        let size = list.layout(
257            SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
258            &mut layout_cx,
259        );
260        assert_eq!(size, vec2f(100., 40.));
261        assert_eq!(
262            state.0.lock().heights.summary(),
263            ElementHeightSummary {
264                count: 3,
265                pending_count: 0,
266                height: 60.
267            }
268        );
269
270        state.splice(1..2, vec![item(40.), item(50.)]);
271        state.splice(3..3, vec![item(60.)]);
272        assert_eq!(
273            state.0.lock().heights.summary(),
274            ElementHeightSummary {
275                count: 5,
276                pending_count: 3,
277                height: 30.
278            }
279        );
280        let size = list.layout(
281            SizeConstraint::new(vec2f(0., 0.), vec2f(100., 40.)),
282            &mut layout_cx,
283        );
284        assert_eq!(size, vec2f(100., 40.));
285        assert_eq!(
286            state.0.lock().heights.summary(),
287            ElementHeightSummary {
288                count: 5,
289                pending_count: 0,
290                height: 180.
291            }
292        );
293    }
294
295    fn item(height: f32) -> ElementBox {
296        ConstrainedBox::new(Empty::new().boxed())
297            .with_height(height)
298            .with_width(100.)
299            .boxed()
300    }
301}