1use super::model_peer::{ModelChangeListener, ModelChangeListenerContainer};
13use super::{Model, ModelExt, ModelRc};
14use crate::item_tree::{ItemTreeVTable, TraversalOrder};
15use crate::layout::Orientation;
16use crate::lengths::{LogicalLength, RectLengths};
17use crate::{Coord, Property};
18use alloc::vec::Vec;
19use core::cell::RefCell;
20use core::pin::Pin;
21#[allow(unused)]
22use euclid::num::Floor;
23use pin_project::pin_project;
24
25type ItemTreeRc<C> = vtable::VRc<crate::item_tree::ItemTreeVTable, C>;
26
27pub trait RepeatedItemTree:
29 crate::item_tree::ItemTree + vtable::HasStaticVTable<ItemTreeVTable> + 'static
30{
31 type Data: Default + 'static;
33
34 fn update(&self, index: usize, data: Self::Data);
36
37 fn init(&self) {}
40
41 fn listview_layout(self: Pin<&Self>, _offset_y: &mut LogicalLength) -> LogicalLength {
48 LogicalLength::default()
49 }
50
51 fn layout_item_info(
54 self: Pin<&Self>,
55 _orientation: Orientation,
56 _child_index: Option<usize>,
57 ) -> crate::layout::LayoutItemInfo {
58 crate::layout::LayoutItemInfo::default()
59 }
60
61 fn layout_item_info_at_cross_width(
66 self: Pin<&Self>,
67 _cross_width: f32,
68 ) -> crate::layout::LayoutItemInfo {
69 self.layout_item_info(Orientation::Vertical, None)
70 }
71
72 fn flexbox_layout_item_info(
75 self: Pin<&Self>,
76 orientation: Orientation,
77 child_index: Option<usize>,
78 ) -> crate::layout::FlexboxLayoutItemInfo {
79 self.layout_item_info(orientation, child_index).into()
80 }
81
82 fn flexbox_layout_item_info_at_cross_width(
87 self: Pin<&Self>,
88 _cross_width: f32,
89 ) -> crate::layout::FlexboxLayoutItemInfo {
90 self.flexbox_layout_item_info(Orientation::Vertical, None)
91 }
92
93 fn grid_layout_input_data(
98 self: Pin<&Self>,
99 _new_row: bool,
100 _result: &mut [crate::layout::GridLayoutInputData],
101 ) {
102 crate::debug_log!(
103 "Internal error in Slint: RepeatedItemTree::grid_layout_input_data() not implemented for {}",
104 core::any::type_name::<Self>()
105 );
106 }
108
109 fn z_order(self: Pin<&Self>) -> Option<f32> {
112 None
113 }
114}
115
116#[derive(Clone, Copy, PartialEq, Debug)]
117enum RepeatedInstanceState {
118 Clean,
120 Dirty,
122}
123struct RepeaterInner<C: RepeatedItemTree> {
124 instances: Vec<(RepeatedInstanceState, Option<ItemTreeRc<C>>)>,
125 layout_state: RepeaterLayoutState,
127}
128
129impl<C: RepeatedItemTree> Default for RepeaterInner<C> {
130 fn default() -> Self {
131 RepeaterInner { instances: Default::default(), layout_state: Default::default() }
132 }
133}
134
135#[derive(Default, Clone, Debug)]
137#[repr(C)]
138pub struct RepeaterLayoutState {
139 pub offset: usize,
141 pub cached_item_height: Coord,
143 pub previous_content_y: Coord,
146 pub anchor_y: Coord,
148}
149
150trait RepeaterInstanceOps {
153 fn len(&self) -> usize;
155
156 fn splice(&mut self, position: usize, remove: usize, add: usize);
158
159 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool;
162
163 fn height(&self, instance_idx: usize) -> Option<Coord>;
165
166 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord;
169}
170
171const MAX_EAGER_INSTANCE_COUNT: usize = 1 << 20;
173
174fn update_all_instances(ops: &mut impl RepeaterInstanceOps, offset: usize, count: usize) {
176 let count = if count > MAX_EAGER_INSTANCE_COUNT {
177 crate::debug_log!("A repeater's model has {count} rows: too many to instantiate");
178 0
179 } else {
180 count
181 };
182 let cur = ops.len();
183 if count > cur {
184 ops.splice(cur, 0, count - cur);
185 } else if count < cur {
186 ops.splice(count, cur - count, 0);
187 }
188 for i in 0..count {
189 ops.ensure_updated(i, i + offset);
190 }
191}
192
193pub trait ListViewProperties {
201 fn content_y_get(&self) -> LogicalLength;
202 fn content_y_get_internal(&self) -> LogicalLength;
205 fn content_y_set(&self, value: LogicalLength);
206 fn content_y_has_binding(&self) -> bool;
207 fn computes_content_height(&self) -> bool;
211 fn content_width_set(&self, value: LogicalLength);
213 fn content_height_set(&self, value: LogicalLength);
215 fn register_as_dependencies(&self);
218}
219
220struct TypedListViewProps<'a> {
221 content_width: Option<Pin<&'a Property<LogicalLength>>>,
222 content_height: Option<Pin<&'a Property<LogicalLength>>>,
223 content_y: Pin<&'a Property<LogicalLength>>,
224}
225
226impl ListViewProperties for TypedListViewProps<'_> {
227 fn content_y_get(&self) -> LogicalLength {
228 self.content_y.get()
229 }
230 fn content_y_get_internal(&self) -> LogicalLength {
231 self.content_y.get_internal()
232 }
233 fn content_y_set(&self, value: LogicalLength) {
234 self.content_y.set(value);
235 }
236 fn content_y_has_binding(&self) -> bool {
237 self.content_y.has_binding()
238 }
239 fn computes_content_height(&self) -> bool {
240 self.content_height.is_some()
241 }
242 fn content_width_set(&self, value: LogicalLength) {
243 if let Some(content_width) = self.content_width {
244 content_width.set(value);
245 }
246 }
247 fn content_height_set(&self, value: LogicalLength) {
248 if let Some(content_height) = self.content_height {
249 content_height.set(value);
250 }
251 }
252 fn register_as_dependencies(&self) {
253 if let Some(content_width) = self.content_width {
254 content_width.register_as_dependency();
255 }
256 if let Some(content_height) = self.content_height {
257 content_height.register_as_dependency();
258 }
259 self.content_y.register_as_dependency();
260 }
261}
262
263fn update_visible_instances(
269 ops: &mut impl RepeaterInstanceOps,
270 state: &mut RepeaterLayoutState,
271 row_count: usize,
272 props: &dyn ListViewProperties,
273 listview_width: LogicalLength,
274 listview_height: LogicalLength,
275) -> bool {
276 let zero = LogicalLength::default();
277 let mut content_width_value = listview_width.get();
278 let listview_height = listview_height.get();
279
280 if row_count == 0 {
281 ops.splice(0, ops.len(), 0);
282 props.content_height_set(zero);
283 props.content_y_set(zero);
284 props.content_width_set(listview_width);
285 return false;
286 }
287
288 let mut content_y_value = props.content_y_get().get();
289 if !props.content_y_has_binding() {
290 content_y_value = content_y_value.min(0 as Coord);
291 }
292
293 let mut changed = false;
294
295 let element_height = if state.cached_item_height > 0 as Coord {
297 state.cached_item_height
298 } else {
299 let mut total_height: Coord = 0 as Coord;
300 let mut count = 0usize;
301 for i in 0..ops.len() {
302 if let Some(h) = ops.height(i) {
303 total_height += h;
304 count += 1;
305 }
306 }
307
308 if count > 0 {
309 total_height / count as Coord
310 } else {
311 state.offset = state.offset.min(row_count - 1);
313 ops.splice(0, ops.len(), 1);
314 changed |= ops.ensure_updated(0, state.offset);
315 ops.height(0).unwrap_or(0 as Coord)
316 }
317 };
318
319 if state.offset >= row_count {
320 state.offset = row_count - 1;
321 }
322
323 let one_and_a_half_screen = listview_height * 3 as Coord / 2 as Coord;
324 let first_item_y = state.anchor_y;
325 let last_item_bottom = first_item_y + element_height * ops.len() as Coord;
326
327 let (mut new_offset, mut new_offset_y) = if first_item_y
328 > -content_y_value + one_and_a_half_screen
329 || (props.computes_content_height() && last_item_bottom + element_height < -content_y_value)
330 {
331 ops.splice(0, ops.len(), 0);
333 state.offset = ((-content_y_value / element_height).floor() as usize).min(row_count - 1);
334 (state.offset, 0 as Coord)
335 } else if content_y_value < state.previous_content_y {
336 let mut it_y = first_item_y + content_y_value;
338 let mut new_off = state.offset;
339 for i in 0..ops.len() {
340 changed |= ops.ensure_updated(i, new_off);
341 let h = ops.height(i).unwrap_or(0 as Coord);
342 if it_y + h > 0 as Coord || new_off + 1 >= row_count {
343 break;
344 }
345 it_y += h;
346 new_off += 1;
347 }
348 (new_off, it_y)
349 } else {
350 (state.offset, first_item_y + content_y_value)
352 };
353
354 let mut loop_count = 0;
355 loop {
356 while new_offset > state.offset && new_offset_y > 0 as Coord {
358 new_offset -= 1;
359 new_offset_y -= ops.height(new_offset - state.offset).unwrap_or(0 as Coord);
360 }
361 let mut prepend_count = 0;
363 while new_offset > 0 && new_offset_y > 0 as Coord {
364 new_offset -= 1;
365 ops.splice(0, 0, 1);
366 changed |= ops.ensure_updated(0, new_offset);
367 new_offset_y -= ops.height(0).unwrap_or(0 as Coord);
368 prepend_count += 1;
369 }
370 if prepend_count > 0 {
371 state.offset = new_offset;
372 }
373 debug_assert!(new_offset >= state.offset && new_offset <= state.offset + ops.len());
374
375 let mut y = new_offset_y;
377 let mut idx = new_offset;
378 let instances_begin = new_offset - state.offset;
379 for i in instances_begin..ops.len() {
380 if idx >= row_count {
381 break;
382 }
383 changed |= ops.ensure_updated(i, idx);
384 content_width_value = content_width_value.max(ops.listview_layout(i, &mut y));
385 idx += 1;
386 if y >= listview_height {
387 break;
388 }
389 }
390
391 while y < listview_height && idx < row_count {
393 let i = ops.len();
394 ops.splice(i, 0, 1);
395 changed |= ops.ensure_updated(i, idx);
396 content_width_value = content_width_value.max(ops.listview_layout(i, &mut y));
397 idx += 1;
398 }
399
400 if y < listview_height && content_y_value < 0 as Coord && loop_count < 3 {
401 debug_assert!(idx >= row_count);
402 content_y_value += listview_height - y;
404 loop_count += 1;
405 continue;
406 }
407
408 if new_offset != state.offset {
410 let remove_count = new_offset - state.offset;
411 ops.splice(0, remove_count, 0);
412 state.offset = new_offset;
413 }
414 let keep = idx - new_offset;
415 if ops.len() > keep {
416 ops.splice(keep, ops.len() - keep, 0);
417 }
418
419 if ops.len() == 0 {
420 break;
421 }
422
423 state.cached_item_height = (y - new_offset_y) / ops.len() as Coord;
425 state.anchor_y = state.cached_item_height * state.offset as Coord;
426 props.content_height_set(LogicalLength::new(state.cached_item_height * row_count as Coord));
427 props.content_width_set(LogicalLength::new(content_width_value));
428 let new_content_y = -state.anchor_y + new_offset_y;
429 if new_content_y != props.content_y_get_internal().get() {
435 props.content_y_set(LogicalLength::new(new_content_y));
439 }
440 state.previous_content_y = new_content_y;
441
442 break;
443 }
444
445 changed
446}
447
448struct RustRepeaterOps<'a, C: RepeatedItemTree> {
450 inner: &'a RefCell<RepeaterInner<C>>,
451 init: &'a dyn Fn() -> ItemTreeRc<C>,
452 model: &'a ModelRc<C::Data>,
453}
454
455impl<C: RepeatedItemTree> RepeaterInstanceOps for RustRepeaterOps<'_, C> {
456 fn len(&self) -> usize {
457 self.inner.borrow().instances.len()
458 }
459
460 fn splice(&mut self, position: usize, remove: usize, add: usize) {
461 self.inner.borrow_mut().instances.splice(
462 position..position + remove,
463 core::iter::repeat_with(|| (RepeatedInstanceState::Dirty, None)).take(add),
464 );
465 }
466
467 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
468 let (created, instance) = {
469 let mut inner = self.inner.borrow_mut();
470 let c = &mut inner.instances[instance_idx];
471 if c.0 != RepeatedInstanceState::Dirty {
472 return false;
473 }
474 let created = c.1.is_none();
475 if created {
476 c.1 = Some((self.init)());
477 }
478 c.1.as_ref().unwrap().update(row, self.model.row_data(row).unwrap_or_default());
479 c.0 = RepeatedInstanceState::Clean;
480 (created, c.1.as_ref().unwrap().clone())
481 };
482 if created {
483 crate::properties::evaluate_no_tracking(|| instance.init());
484 }
485 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(instance));
486 created
487 }
488
489 fn height(&self, instance_idx: usize) -> Option<Coord> {
490 self.inner.borrow().instances[instance_idx]
491 .1
492 .as_ref()
493 .map(|x| x.as_pin_ref().item_geometry(0).height_length().get())
494 }
495
496 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
497 let inner = self.inner.borrow();
498 let mut y_len = LogicalLength::new(*y);
499 let w = inner.instances[instance_idx]
500 .1
501 .as_ref()
502 .unwrap()
503 .as_pin_ref()
504 .listview_layout(&mut y_len);
505 *y = y_len.get();
506 w.get()
507 }
508}
509
510#[pin_project]
513pub struct RepeaterTracker<T: RepeatedItemTree> {
514 inner: RefCell<RepeaterInner<T>>,
515 #[pin]
516 model: Property<ModelRc<T::Data>>,
517 #[pin]
518 is_dirty: Property<bool>,
520 #[pin]
521 instance_generation: Property<()>,
526 #[pin]
528 listview_geometry_tracker: crate::properties::PropertyTracker,
529}
530
531impl<T: RepeatedItemTree> ModelChangeListener for RepeaterTracker<T> {
532 fn row_changed(self: Pin<&Self>, row: usize) {
534 let mut inner = self.inner.borrow_mut();
535 let inner = &mut *inner;
536 if let Some(c) = inner.instances.get_mut(row.wrapping_sub(inner.layout_state.offset)) {
537 if !self.model.is_dirty() {
538 if let Some(comp) = c.1.as_ref() {
539 let model = self.project_ref().model.get_untracked();
540 comp.update(row, model.row_data(row).unwrap_or_default());
541 c.0 = RepeatedInstanceState::Clean;
542 }
543 } else {
544 c.0 = RepeatedInstanceState::Dirty;
545 }
546 }
547 }
548 fn row_added(self: Pin<&Self>, mut index: usize, mut count: usize) {
550 let mut inner = self.inner.borrow_mut();
551 if index < inner.layout_state.offset {
552 if index + count <= inner.layout_state.offset {
553 inner.layout_state.offset += count;
555 self.is_dirty.set(true);
556 for c in inner.instances.iter_mut() {
557 c.0 = RepeatedInstanceState::Dirty;
558 }
559 return;
560 }
561 count -= inner.layout_state.offset - index;
562 index = 0;
563 } else {
564 index -= inner.layout_state.offset;
565 }
566 if count == 0 || index > inner.instances.len() {
567 return;
568 }
569 self.is_dirty.set(true);
570 inner.instances.splice(
571 index..index,
572 core::iter::repeat_n((RepeatedInstanceState::Dirty, None), count),
573 );
574 for c in inner.instances[index + count..].iter_mut() {
575 c.0 = RepeatedInstanceState::Dirty;
577 }
578 }
579 fn row_removed(self: Pin<&Self>, mut index: usize, mut count: usize) {
581 let mut inner = self.inner.borrow_mut();
582 if index < inner.layout_state.offset {
583 if index + count <= inner.layout_state.offset {
584 inner.layout_state.offset -= count;
586 self.is_dirty.set(true);
587 for c in inner.instances.iter_mut() {
588 c.0 = RepeatedInstanceState::Dirty;
589 }
590 return;
591 }
592 count -= inner.layout_state.offset - index;
593 inner.layout_state.offset = index;
594 index = 0;
595 } else {
596 index -= inner.layout_state.offset;
597 }
598 if count == 0 || index >= inner.instances.len() {
599 return;
600 }
601 if (index + count) > inner.instances.len() {
602 count = inner.instances.len() - index;
603 }
604 self.is_dirty.set(true);
605 inner.instances.drain(index..(index + count));
606 for c in inner.instances[index..].iter_mut() {
607 c.0 = RepeatedInstanceState::Dirty;
609 }
610 }
611
612 fn reset(self: Pin<&Self>) {
613 self.is_dirty.set(true);
614 self.inner.borrow_mut().instances.clear();
615 }
616}
617
618impl<C: RepeatedItemTree> Default for RepeaterTracker<C> {
619 fn default() -> Self {
620 Self {
621 inner: Default::default(),
622 model: Property::new_named(ModelRc::default(), "i_slint_core::Repeater::model"),
623 is_dirty: Property::new_named(false, "i_slint_core::Repeater::is_dirty"),
624 instance_generation: Property::new_named(
625 (),
626 "i_slint_core::Repeater::instance_generation",
627 ),
628 listview_geometry_tracker: Default::default(),
629 }
630 }
631}
632
633#[pin_project]
634pub struct Repeater<C: RepeatedItemTree>(#[pin] ModelChangeListenerContainer<RepeaterTracker<C>>);
635
636impl<C: RepeatedItemTree> Default for Repeater<C> {
637 fn default() -> Self {
638 Self(Default::default())
639 }
640}
641
642impl<C: RepeatedItemTree + 'static> Repeater<C> {
643 fn data(self: Pin<&Self>) -> Pin<&RepeaterTracker<C>> {
644 self.project_ref().0.get()
645 }
646
647 pub fn track_model_changes(self: Pin<&Self>) {
651 self.data().project_ref().model.register_as_dependency();
652 self.data().project_ref().is_dirty.register_as_dependency();
653 }
654
655 pub fn track_instance_changes(self: Pin<&Self>) {
660 self.data().project_ref().instance_generation.register_as_dependency();
661 }
662
663 fn model(self: Pin<&Self>) -> ModelRc<C::Data> {
664 let model = self.data().project_ref().model;
665
666 if model.is_dirty() {
667 let old_model = model.get_internal();
668 let m = model.get();
669 if old_model != m {
670 *self.data().inner.borrow_mut() = RepeaterInner::default();
671 self.data().is_dirty.set(true);
672 let peer = self.project_ref().0.model_peer();
673 m.model_tracker().attach_peer(peer);
674 }
675 m
676 } else {
677 model.get()
678 }
679 }
680
681 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) -> bool {
686 let model = self.model();
687 let changed = if self.data().project_ref().is_dirty.get() {
688 let count = model.row_count();
689 let offset = self.0.inner.borrow().layout_state.offset;
690 let mut ops = RustRepeaterOps { inner: &self.0.inner, init: &init, model: &model };
691 self.data().is_dirty.set(false);
692 update_all_instances(&mut ops, offset, count);
693 self.data().instance_generation.mark_dirty();
694 true
695 } else {
696 false
697 };
698 self.ensure_children_instantiated() || changed
699 }
700
701 fn ensure_children_instantiated(&self) -> bool {
703 let mut changed = false;
704 for instance in self.instances_vec() {
705 changed |=
706 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(instance));
707 }
708 changed
709 }
710
711 pub fn track_changes_listview(
715 self: Pin<&Self>,
716 content_width: Option<Pin<&Property<LogicalLength>>>,
717 content_height: Option<Pin<&Property<LogicalLength>>>,
718 content_y: Pin<&Property<LogicalLength>>,
719 listview_width: LogicalLength,
720 listview_height: Pin<&Property<LogicalLength>>,
721 ) {
722 let props = TypedListViewProps { content_width, content_height, content_y };
723 self.track_changes_listview_callback(&props, listview_width);
724 listview_height.register_as_dependency();
725 }
726
727 pub fn track_changes_listview_callback(
732 self: Pin<&Self>,
733 props: &dyn ListViewProperties,
734 listview_width: LogicalLength,
735 ) {
736 props.register_as_dependencies();
737 let _ = listview_width;
741 }
742
743 pub fn ensure_updated_listview(
746 self: Pin<&Self>,
747 init: impl Fn() -> ItemTreeRc<C>,
748 content_width: Option<Pin<&Property<LogicalLength>>>,
749 content_height: Option<Pin<&Property<LogicalLength>>>,
750 content_y: Pin<&Property<LogicalLength>>,
751 listview_width: LogicalLength,
752 listview_height: Pin<&Property<LogicalLength>>,
753 ) -> bool {
754 let props = TypedListViewProps { content_width, content_height, content_y };
755 self.ensure_updated_listview_callback(init, &props, listview_width, listview_height.get())
756 }
757
758 pub fn ensure_updated_listview_callback(
764 self: Pin<&Self>,
765 init: impl Fn() -> ItemTreeRc<C>,
766 props: &dyn ListViewProperties,
767 listview_width: LogicalLength,
768 listview_height: LogicalLength,
769 ) -> bool {
770 self.data().project_ref().is_dirty.set(false);
771
772 let model = self.model();
773 let row_count = model.row_count();
774
775 let data = self.data();
776 let mut layout_state = data.inner.borrow().layout_state.clone();
777 let mut ops = RustRepeaterOps { inner: &data.inner, init: &init, model: &model };
778 let changed = update_visible_instances(
779 &mut ops,
780 &mut layout_state,
781 row_count,
782 props,
783 listview_width,
784 listview_height,
785 );
786 data.inner.borrow_mut().layout_state = layout_state;
787
788 if changed {
789 self.data().instance_generation.mark_dirty();
790 }
791 self.ensure_children_instantiated() || changed
792 }
793
794 pub fn model_set_row_data(self: Pin<&Self>, row: usize, data: C::Data) {
796 let model = self.model();
797 model.set_row_data(row, data);
798 }
799
800 pub fn model_row_data(self: Pin<&Self>, row: usize) -> Option<C::Data> {
803 self.model().row_data_tracked(row)
804 }
805
806 pub fn set_model_binding(&self, binding: impl Fn() -> ModelRc<C::Data> + 'static) {
808 self.0.model.set_binding(binding);
809 }
810
811 pub fn visit(
815 self: Pin<&Self>,
816 order: TraversalOrder,
817 mut visitor: crate::item_tree::ItemVisitorRefMut,
818 ) -> crate::item_tree::VisitChildrenResult {
819 self.track_model_changes();
820 let count = self.0.inner.borrow().instances.len() as u32;
822 for i in 0..count {
823 let i = if order == TraversalOrder::BackToFront { i } else { count - i - 1 };
824 let c = self.0.inner.borrow().instances.get(i as usize).and_then(|c| c.1.clone());
825 if let Some(c) = c
826 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
827 {
828 return crate::item_tree::VisitChildrenResult::abort(i, 0);
829 }
830 }
831 crate::item_tree::VisitChildrenResult::CONTINUE
832 }
833
834 pub fn for_each_instance_z(self: Pin<&Self>, cb: &mut dyn FnMut(u32, f32)) {
840 self.track_model_changes();
841 let (offset, instances): (usize, Vec<_>) = {
844 let inner = self.0.inner.borrow();
845 (inner.layout_state.offset, inner.instances.iter().map(|c| c.1.clone()).collect())
846 };
847 for (i, c) in instances.iter().enumerate() {
848 let z = c.as_ref().and_then(|c| c.as_pin_ref().z_order()).unwrap_or_default();
849 cb((offset + i) as u32, z);
850 }
851 }
852
853 pub fn len(&self) -> usize {
855 self.0.inner.borrow().instances.len()
856 }
857
858 pub fn range(&self) -> core::ops::Range<usize> {
863 let inner = self.0.inner.borrow();
864 core::ops::Range {
865 start: inner.layout_state.offset,
866 end: inner.layout_state.offset + inner.instances.len(),
867 }
868 }
869
870 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
873 let inner = self.0.inner.borrow();
874 inner.instances.get(index.checked_sub(inner.layout_state.offset)?).and_then(|c| c.1.clone())
875 }
876
877 pub fn is_empty(&self) -> bool {
879 self.len() == 0
880 }
881
882 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
884 self.0.inner.borrow().instances.iter().flat_map(|x| x.1.clone()).collect()
885 }
886}
887
888#[pin_project]
889pub struct Conditional<C: RepeatedItemTree> {
890 #[pin]
891 model: Property<bool>,
892 #[pin]
893 instance_generation: Property<()>,
894 instance: RefCell<Option<ItemTreeRc<C>>>,
895}
896
897impl<C: RepeatedItemTree> Default for Conditional<C> {
898 fn default() -> Self {
899 Self {
900 model: Property::new_named(false, "i_slint_core::Conditional::model"),
901 instance_generation: Property::new_named(
902 (),
903 "i_slint_core::Conditional::instance_generation",
904 ),
905 instance: RefCell::new(None),
906 }
907 }
908}
909
910impl<C: RepeatedItemTree + 'static> Conditional<C> {
911 pub fn track_model_changes(self: Pin<&Self>) {
914 self.project_ref().model.register_as_dependency();
915 }
916
917 pub fn track_instance_changes(self: Pin<&Self>) {
921 self.project_ref().instance_generation.register_as_dependency();
922 }
923
924 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) -> bool {
928 let model = self.project_ref().model.get();
929
930 let changed = if !model {
931 self.instance.take().is_some()
932 } else if self.instance.borrow().is_none() {
933 let i = init();
934 self.instance.replace(Some(i.clone()));
935 i.init();
936 true
937 } else {
938 false
939 };
940 if changed {
941 self.instance_generation.mark_dirty();
942 }
943 if let Some(instance) = self.instance.borrow().as_ref() {
944 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(
945 instance.clone(),
946 )) || changed
947 } else {
948 changed
949 }
950 }
951
952 pub fn set_model_binding(&self, binding: impl Fn() -> bool + 'static) {
954 self.model.set_binding(binding);
955 }
956
957 pub fn visit(
961 self: Pin<&Self>,
962 order: TraversalOrder,
963 mut visitor: crate::item_tree::ItemVisitorRefMut,
964 ) -> crate::item_tree::VisitChildrenResult {
965 self.track_model_changes();
966 let instance = self.instance.borrow().clone();
968 if let Some(c) = instance
969 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
970 {
971 return crate::item_tree::VisitChildrenResult::abort(0, 0);
972 }
973
974 crate::item_tree::VisitChildrenResult::CONTINUE
975 }
976
977 pub fn for_each_instance_z(self: Pin<&Self>, cb: &mut dyn FnMut(u32, f32)) {
981 self.track_model_changes();
982 let instance = self.instance.borrow().clone();
983 if let Some(c) = instance {
984 cb(0, c.as_pin_ref().z_order().unwrap_or_default());
985 }
986 }
987
988 pub fn len(&self) -> usize {
990 self.instance.borrow().is_some() as usize
991 }
992
993 pub fn range(&self) -> core::ops::Range<usize> {
997 0..self.len()
998 }
999
1000 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
1003 if index != 0 {
1004 return None;
1005 }
1006 self.instance.borrow().clone()
1007 }
1008
1009 pub fn is_empty(&self) -> bool {
1011 self.len() == 0
1012 }
1013
1014 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
1016 self.instance.borrow().clone().into_iter().collect()
1017 }
1018}
1019
1020#[cfg(feature = "ffi")]
1021mod ffi {
1022 #![allow(unsafe_code)]
1023
1024 use super::*;
1025
1026 #[repr(C)]
1029 pub struct RepeaterInstanceOpsVTable {
1030 pub user_data: *mut core::ffi::c_void,
1031 pub len: unsafe extern "C" fn(user_data: *mut core::ffi::c_void) -> usize,
1032 pub splice: unsafe extern "C" fn(
1033 user_data: *mut core::ffi::c_void,
1034 position: usize,
1035 remove: usize,
1036 add: usize,
1037 ),
1038 pub ensure_updated: unsafe extern "C" fn(
1039 user_data: *mut core::ffi::c_void,
1040 instance_idx: usize,
1041 row: usize,
1042 ) -> bool,
1043 pub height:
1045 unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize) -> Coord,
1046 pub listview_layout: Option<
1047 unsafe extern "C" fn(
1048 user_data: *mut core::ffi::c_void,
1049 instance_idx: usize,
1050 y: &mut Coord,
1051 ) -> Coord,
1052 >,
1053 pub init: unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize),
1054 }
1055
1056 impl RepeaterInstanceOps for RepeaterInstanceOpsVTable {
1057 fn len(&self) -> usize {
1058 unsafe { (self.len)(self.user_data) }
1059 }
1060 fn splice(&mut self, position: usize, remove: usize, add: usize) {
1061 unsafe { (self.splice)(self.user_data, position, remove, add) }
1062 }
1063 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
1064 let created = unsafe { (self.ensure_updated)(self.user_data, instance_idx, row) };
1065 if created {
1066 unsafe { (self.init)(self.user_data, instance_idx) };
1067 }
1068 created
1069 }
1070 fn height(&self, instance_idx: usize) -> Option<Coord> {
1071 let h = unsafe { (self.height)(self.user_data, instance_idx) };
1072 if h.is_nan() { None } else { Some(h) }
1073 }
1074 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
1075 self.listview_layout
1076 .map_or(0 as Coord, |f| unsafe { f(self.user_data, instance_idx, y) })
1077 }
1078 }
1079
1080 #[unsafe(no_mangle)]
1081 pub extern "C" fn slint_repeater_ensure_updated(
1082 ops: &mut RepeaterInstanceOpsVTable,
1083 offset: usize,
1084 count: usize,
1085 ) {
1086 update_all_instances(ops, offset, count);
1087 }
1088
1089 #[unsafe(no_mangle)]
1090 pub extern "C" fn slint_repeater_ensure_updated_listview(
1091 ops: &mut RepeaterInstanceOpsVTable,
1092 state: &mut RepeaterLayoutState,
1093 row_count: usize,
1094 content_width: Option<Pin<&Property<LogicalLength>>>,
1095 content_height: Option<Pin<&Property<LogicalLength>>>,
1096 content_y: Pin<&Property<LogicalLength>>,
1097 listview_width: LogicalLength,
1098 listview_height: LogicalLength,
1099 ) -> bool {
1100 let props = TypedListViewProps { content_width, content_height, content_y };
1101 update_visible_instances(ops, state, row_count, &props, listview_width, listview_height)
1102 }
1103}