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}