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 flexbox_layout_item_info(
64 self: Pin<&Self>,
65 orientation: Orientation,
66 child_index: Option<usize>,
67 ) -> crate::layout::FlexboxLayoutItemInfo {
68 self.layout_item_info(orientation, child_index).into()
69 }
70
71 fn grid_layout_input_data(
76 self: Pin<&Self>,
77 _new_row: bool,
78 _result: &mut [crate::layout::GridLayoutInputData],
79 ) {
80 crate::debug_log!(
81 "Internal error in Slint: RepeatedItemTree::grid_layout_input_data() not implemented for {}",
82 core::any::type_name::<Self>()
83 );
84 }
86}
87
88#[derive(Clone, Copy, PartialEq, Debug)]
89enum RepeatedInstanceState {
90 Clean,
92 Dirty,
94}
95struct RepeaterInner<C: RepeatedItemTree> {
96 instances: Vec<(RepeatedInstanceState, Option<ItemTreeRc<C>>)>,
97 layout_state: RepeaterLayoutState,
99}
100
101impl<C: RepeatedItemTree> Default for RepeaterInner<C> {
102 fn default() -> Self {
103 RepeaterInner { instances: Default::default(), layout_state: Default::default() }
104 }
105}
106
107#[derive(Default, Clone, Debug)]
109#[repr(C)]
110pub struct RepeaterLayoutState {
111 pub offset: usize,
113 pub cached_item_height: Coord,
115 pub previous_viewport_y: Coord,
118 pub anchor_y: Coord,
120}
121
122trait RepeaterInstanceOps {
125 fn len(&self) -> usize;
127
128 fn splice(&mut self, position: usize, remove: usize, add: usize);
130
131 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool;
134
135 fn height(&self, instance_idx: usize) -> Option<Coord>;
137
138 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord;
141}
142
143fn update_all_instances(ops: &mut impl RepeaterInstanceOps, offset: usize, count: usize) {
145 let cur = ops.len();
146 if count > cur {
147 ops.splice(cur, 0, count - cur);
148 } else if count < cur {
149 ops.splice(count, cur - count, 0);
150 }
151 for i in 0..count {
152 ops.ensure_updated(i, i + offset);
153 }
154}
155
156fn update_visible_instances(
162 ops: &mut impl RepeaterInstanceOps,
163 state: &mut RepeaterLayoutState,
164 row_count: usize,
165 viewport_width: Pin<&Property<LogicalLength>>,
166 viewport_height: Pin<&Property<LogicalLength>>,
167 viewport_y: Pin<&Property<LogicalLength>>,
168 listview_width: LogicalLength,
169 listview_height: LogicalLength,
170) -> bool {
171 let zero = LogicalLength::default();
172 let mut vp_width = listview_width.get();
173 let listview_height = listview_height.get();
174
175 if row_count == 0 {
176 ops.splice(0, ops.len(), 0);
177 viewport_height.set(zero);
178 viewport_y.set(zero);
179 viewport_width.set(listview_width);
180 return false;
181 }
182
183 let mut vp_y = viewport_y.get().get();
184 if !viewport_y.has_binding() {
185 vp_y = vp_y.min(0 as Coord);
186 }
187
188 let mut changed = false;
189
190 let element_height = if state.cached_item_height > 0 as Coord {
192 state.cached_item_height
193 } else {
194 let mut total_height: Coord = 0 as Coord;
195 let mut count = 0usize;
196 for i in 0..ops.len() {
197 if let Some(h) = ops.height(i) {
198 total_height += h;
199 count += 1;
200 }
201 }
202
203 if count > 0 {
204 total_height / count as Coord
205 } else {
206 state.offset = state.offset.min(row_count - 1);
208 ops.splice(0, ops.len(), 1);
209 changed |= ops.ensure_updated(0, state.offset);
210 ops.height(0).unwrap_or(0 as Coord)
211 }
212 };
213
214 if state.offset >= row_count {
215 state.offset = row_count - 1;
216 }
217
218 let one_and_a_half_screen = listview_height * 3 as Coord / 2 as Coord;
219 let first_item_y = state.anchor_y;
220 let last_item_bottom = first_item_y + element_height * ops.len() as Coord;
221
222 let (mut new_offset, mut new_offset_y) = if first_item_y > -vp_y + one_and_a_half_screen
223 || last_item_bottom + element_height < -vp_y
224 {
225 ops.splice(0, ops.len(), 0);
227 state.offset = ((-vp_y / element_height).floor() as usize).min(row_count - 1);
228 (state.offset, 0 as Coord)
229 } else if vp_y < state.previous_viewport_y {
230 let mut it_y = first_item_y + vp_y;
232 let mut new_off = state.offset;
233 for i in 0..ops.len() {
234 changed |= ops.ensure_updated(i, new_off);
235 let h = ops.height(i).unwrap_or(0 as Coord);
236 if it_y + h > 0 as Coord || new_off + 1 >= row_count {
237 break;
238 }
239 it_y += h;
240 new_off += 1;
241 }
242 (new_off, it_y)
243 } else {
244 (state.offset, first_item_y + vp_y)
246 };
247
248 let mut loop_count = 0;
249 loop {
250 while new_offset > state.offset && new_offset_y > 0 as Coord {
252 new_offset -= 1;
253 new_offset_y -= ops.height(new_offset - state.offset).unwrap_or(0 as Coord);
254 }
255 let mut prepend_count = 0;
257 while new_offset > 0 && new_offset_y > 0 as Coord {
258 new_offset -= 1;
259 ops.splice(0, 0, 1);
260 changed |= ops.ensure_updated(0, new_offset);
261 new_offset_y -= ops.height(0).unwrap_or(0 as Coord);
262 prepend_count += 1;
263 }
264 if prepend_count > 0 {
265 state.offset = new_offset;
266 }
267 debug_assert!(new_offset >= state.offset && new_offset <= state.offset + ops.len());
268
269 let mut y = new_offset_y;
271 let mut idx = new_offset;
272 let instances_begin = new_offset - state.offset;
273 for i in instances_begin..ops.len() {
274 if idx >= row_count {
275 break;
276 }
277 changed |= ops.ensure_updated(i, idx);
278 vp_width = vp_width.max(ops.listview_layout(i, &mut y));
279 idx += 1;
280 if y >= listview_height {
281 break;
282 }
283 }
284
285 while y < listview_height && idx < row_count {
287 let i = ops.len();
288 ops.splice(i, 0, 1);
289 changed |= ops.ensure_updated(i, idx);
290 vp_width = vp_width.max(ops.listview_layout(i, &mut y));
291 idx += 1;
292 }
293
294 if y < listview_height && vp_y < 0 as Coord && loop_count < 3 {
295 debug_assert!(idx >= row_count);
296 vp_y += listview_height - y;
298 loop_count += 1;
299 continue;
300 }
301
302 if new_offset != state.offset {
304 let remove_count = new_offset - state.offset;
305 ops.splice(0, remove_count, 0);
306 state.offset = new_offset;
307 }
308 let keep = idx - new_offset;
309 if ops.len() > keep {
310 ops.splice(keep, ops.len() - keep, 0);
311 }
312
313 if ops.len() == 0 {
314 break;
315 }
316
317 state.cached_item_height = (y - new_offset_y) / ops.len() as Coord;
319 state.anchor_y = state.cached_item_height * state.offset as Coord;
320 viewport_height.set(LogicalLength::new(state.cached_item_height * row_count as Coord));
321 viewport_width.set(LogicalLength::new(vp_width));
322 let new_viewport_y = -state.anchor_y + new_offset_y;
323 if new_viewport_y != viewport_y.get_internal().get() {
329 viewport_y.set(LogicalLength::new(new_viewport_y));
333 }
334 state.previous_viewport_y = new_viewport_y;
335
336 break;
337 }
338
339 changed
340}
341
342struct RustRepeaterOps<'a, C: RepeatedItemTree> {
344 inner: &'a RefCell<RepeaterInner<C>>,
345 init: &'a dyn Fn() -> ItemTreeRc<C>,
346 model: &'a ModelRc<C::Data>,
347}
348
349impl<C: RepeatedItemTree> RepeaterInstanceOps for RustRepeaterOps<'_, C> {
350 fn len(&self) -> usize {
351 self.inner.borrow().instances.len()
352 }
353
354 fn splice(&mut self, position: usize, remove: usize, add: usize) {
355 self.inner.borrow_mut().instances.splice(
356 position..position + remove,
357 core::iter::repeat_with(|| (RepeatedInstanceState::Dirty, None)).take(add),
358 );
359 }
360
361 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
362 let (created, instance) = {
363 let mut inner = self.inner.borrow_mut();
364 let c = &mut inner.instances[instance_idx];
365 if c.0 != RepeatedInstanceState::Dirty {
366 return false;
367 }
368 let created = c.1.is_none();
369 if created {
370 c.1 = Some((self.init)());
371 }
372 c.1.as_ref().unwrap().update(row, self.model.row_data(row).unwrap_or_default());
373 c.0 = RepeatedInstanceState::Clean;
374 (created, c.1.as_ref().unwrap().clone())
375 };
376 if created {
377 crate::properties::evaluate_no_tracking(|| instance.init());
378 }
379 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(instance));
380 created
381 }
382
383 fn height(&self, instance_idx: usize) -> Option<Coord> {
384 self.inner.borrow().instances[instance_idx]
385 .1
386 .as_ref()
387 .map(|x| x.as_pin_ref().item_geometry(0).height_length().get())
388 }
389
390 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
391 let inner = self.inner.borrow();
392 let mut y_len = LogicalLength::new(*y);
393 let w = inner.instances[instance_idx]
394 .1
395 .as_ref()
396 .unwrap()
397 .as_pin_ref()
398 .listview_layout(&mut y_len);
399 *y = y_len.get();
400 w.get()
401 }
402}
403
404#[pin_project]
407pub struct RepeaterTracker<T: RepeatedItemTree> {
408 inner: RefCell<RepeaterInner<T>>,
409 #[pin]
410 model: Property<ModelRc<T::Data>>,
411 #[pin]
412 is_dirty: Property<bool>,
414 #[pin]
415 instance_generation: Property<()>,
420 #[pin]
422 listview_geometry_tracker: crate::properties::PropertyTracker,
423}
424
425impl<T: RepeatedItemTree> ModelChangeListener for RepeaterTracker<T> {
426 fn row_changed(self: Pin<&Self>, row: usize) {
428 let mut inner = self.inner.borrow_mut();
429 let inner = &mut *inner;
430 if let Some(c) = inner.instances.get_mut(row.wrapping_sub(inner.layout_state.offset)) {
431 if !self.model.is_dirty() {
432 if let Some(comp) = c.1.as_ref() {
433 let model = self.project_ref().model.get_untracked();
434 comp.update(row, model.row_data(row).unwrap_or_default());
435 c.0 = RepeatedInstanceState::Clean;
436 }
437 } else {
438 c.0 = RepeatedInstanceState::Dirty;
439 }
440 }
441 }
442 fn row_added(self: Pin<&Self>, mut index: usize, mut count: usize) {
444 let mut inner = self.inner.borrow_mut();
445 if index < inner.layout_state.offset {
446 if index + count <= inner.layout_state.offset {
447 inner.layout_state.offset += count;
449 self.is_dirty.set(true);
450 for c in inner.instances.iter_mut() {
451 c.0 = RepeatedInstanceState::Dirty;
452 }
453 return;
454 }
455 count -= inner.layout_state.offset - index;
456 index = 0;
457 } else {
458 index -= inner.layout_state.offset;
459 }
460 if count == 0 || index > inner.instances.len() {
461 return;
462 }
463 self.is_dirty.set(true);
464 inner.instances.splice(
465 index..index,
466 core::iter::repeat_n((RepeatedInstanceState::Dirty, None), count),
467 );
468 for c in inner.instances[index + count..].iter_mut() {
469 c.0 = RepeatedInstanceState::Dirty;
471 }
472 }
473 fn row_removed(self: Pin<&Self>, mut index: usize, mut count: usize) {
475 let mut inner = self.inner.borrow_mut();
476 if index < inner.layout_state.offset {
477 if index + count <= inner.layout_state.offset {
478 inner.layout_state.offset -= count;
480 self.is_dirty.set(true);
481 for c in inner.instances.iter_mut() {
482 c.0 = RepeatedInstanceState::Dirty;
483 }
484 return;
485 }
486 count -= inner.layout_state.offset - index;
487 inner.layout_state.offset = index;
488 index = 0;
489 } else {
490 index -= inner.layout_state.offset;
491 }
492 if count == 0 || index >= inner.instances.len() {
493 return;
494 }
495 if (index + count) > inner.instances.len() {
496 count = inner.instances.len() - index;
497 }
498 self.is_dirty.set(true);
499 inner.instances.drain(index..(index + count));
500 for c in inner.instances[index..].iter_mut() {
501 c.0 = RepeatedInstanceState::Dirty;
503 }
504 }
505
506 fn reset(self: Pin<&Self>) {
507 self.is_dirty.set(true);
508 self.inner.borrow_mut().instances.clear();
509 }
510}
511
512impl<C: RepeatedItemTree> Default for RepeaterTracker<C> {
513 fn default() -> Self {
514 Self {
515 inner: Default::default(),
516 model: Property::new_named(ModelRc::default(), "i_slint_core::Repeater::model"),
517 is_dirty: Property::new_named(false, "i_slint_core::Repeater::is_dirty"),
518 instance_generation: Property::new_named(
519 (),
520 "i_slint_core::Repeater::instance_generation",
521 ),
522 listview_geometry_tracker: Default::default(),
523 }
524 }
525}
526
527#[pin_project]
528pub struct Repeater<C: RepeatedItemTree>(#[pin] ModelChangeListenerContainer<RepeaterTracker<C>>);
529
530impl<C: RepeatedItemTree> Default for Repeater<C> {
531 fn default() -> Self {
532 Self(Default::default())
533 }
534}
535
536impl<C: RepeatedItemTree + 'static> Repeater<C> {
537 fn data(self: Pin<&Self>) -> Pin<&RepeaterTracker<C>> {
538 self.project_ref().0.get()
539 }
540
541 pub fn track_model_changes(self: Pin<&Self>) {
545 self.data().project_ref().model.register_as_dependency();
546 self.data().project_ref().is_dirty.register_as_dependency();
547 }
548
549 pub fn track_instance_changes(self: Pin<&Self>) {
554 self.data().project_ref().instance_generation.register_as_dependency();
555 }
556
557 fn model(self: Pin<&Self>) -> ModelRc<C::Data> {
558 let model = self.data().project_ref().model;
559
560 if model.is_dirty() {
561 let old_model = model.get_internal();
562 let m = model.get();
563 if old_model != m {
564 *self.data().inner.borrow_mut() = RepeaterInner::default();
565 self.data().is_dirty.set(true);
566 let peer = self.project_ref().0.model_peer();
567 m.model_tracker().attach_peer(peer);
568 }
569 m
570 } else {
571 model.get()
572 }
573 }
574
575 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) -> bool {
580 let model = self.model();
581 let changed = if self.data().project_ref().is_dirty.get() {
582 let count = model.row_count();
583 let offset = self.0.inner.borrow().layout_state.offset;
584 let mut ops = RustRepeaterOps { inner: &self.0.inner, init: &init, model: &model };
585 self.data().is_dirty.set(false);
586 update_all_instances(&mut ops, offset, count);
587 self.data().instance_generation.mark_dirty();
588 true
589 } else {
590 false
591 };
592 self.ensure_children_instantiated() || changed
593 }
594
595 fn ensure_children_instantiated(&self) -> bool {
597 let mut changed = false;
598 for instance in self.instances_vec() {
599 changed |=
600 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(instance));
601 }
602 changed
603 }
604
605 pub fn track_changes_listview(
609 self: Pin<&Self>,
610 viewport_width: Pin<&Property<LogicalLength>>,
611 viewport_height: Pin<&Property<LogicalLength>>,
612 viewport_y: Pin<&Property<LogicalLength>>,
613 listview_width: LogicalLength,
614 listview_height: Pin<&Property<LogicalLength>>,
615 ) {
616 viewport_width.register_as_dependency();
617 viewport_height.register_as_dependency();
618 viewport_y.register_as_dependency();
619 let _ = listview_width;
623 listview_height.register_as_dependency();
624 }
625
626 pub fn ensure_updated_listview(
629 self: Pin<&Self>,
630 init: impl Fn() -> ItemTreeRc<C>,
631 viewport_width: Pin<&Property<LogicalLength>>,
632 viewport_height: Pin<&Property<LogicalLength>>,
633 viewport_y: Pin<&Property<LogicalLength>>,
634 listview_width: LogicalLength,
635 listview_height: Pin<&Property<LogicalLength>>,
636 ) -> bool {
637 self.data().project_ref().is_dirty.set(false);
638
639 let model = self.model();
640 let row_count = model.row_count();
641
642 let data = self.data();
643 let mut layout_state = data.inner.borrow().layout_state.clone();
644 let mut ops = RustRepeaterOps { inner: &data.inner, init: &init, model: &model };
645 let changed = update_visible_instances(
646 &mut ops,
647 &mut layout_state,
648 row_count,
649 viewport_width,
650 viewport_height,
651 viewport_y,
652 listview_width,
653 listview_height.get(),
654 );
655 data.inner.borrow_mut().layout_state = layout_state;
656
657 if changed {
658 self.data().instance_generation.mark_dirty();
659 }
660 self.ensure_children_instantiated() || changed
661 }
662
663 pub fn model_set_row_data(self: Pin<&Self>, row: usize, data: C::Data) {
665 let model = self.model();
666 model.set_row_data(row, data);
667 }
668
669 pub fn model_row_data(self: Pin<&Self>, row: usize) -> Option<C::Data> {
672 self.model().row_data_tracked(row)
673 }
674
675 pub fn set_model_binding(&self, binding: impl Fn() -> ModelRc<C::Data> + 'static) {
677 self.0.model.set_binding(binding);
678 }
679
680 pub fn visit(
684 self: Pin<&Self>,
685 order: TraversalOrder,
686 mut visitor: crate::item_tree::ItemVisitorRefMut,
687 ) -> crate::item_tree::VisitChildrenResult {
688 self.track_model_changes();
689 let count = self.0.inner.borrow().instances.len() as u32;
691 for i in 0..count {
692 let i = if order == TraversalOrder::BackToFront { i } else { count - i - 1 };
693 let c = self.0.inner.borrow().instances.get(i as usize).and_then(|c| c.1.clone());
694 if let Some(c) = c
695 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
696 {
697 return crate::item_tree::VisitChildrenResult::abort(i, 0);
698 }
699 }
700 crate::item_tree::VisitChildrenResult::CONTINUE
701 }
702
703 pub fn len(&self) -> usize {
705 self.0.inner.borrow().instances.len()
706 }
707
708 pub fn range(&self) -> core::ops::Range<usize> {
713 let inner = self.0.inner.borrow();
714 core::ops::Range {
715 start: inner.layout_state.offset,
716 end: inner.layout_state.offset + inner.instances.len(),
717 }
718 }
719
720 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
723 let inner = self.0.inner.borrow();
724 inner.instances.get(index.checked_sub(inner.layout_state.offset)?).and_then(|c| c.1.clone())
725 }
726
727 pub fn is_empty(&self) -> bool {
729 self.len() == 0
730 }
731
732 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
734 self.0.inner.borrow().instances.iter().flat_map(|x| x.1.clone()).collect()
735 }
736}
737
738#[pin_project]
739pub struct Conditional<C: RepeatedItemTree> {
740 #[pin]
741 model: Property<bool>,
742 #[pin]
743 instance_generation: Property<()>,
744 instance: RefCell<Option<ItemTreeRc<C>>>,
745}
746
747impl<C: RepeatedItemTree> Default for Conditional<C> {
748 fn default() -> Self {
749 Self {
750 model: Property::new_named(false, "i_slint_core::Conditional::model"),
751 instance_generation: Property::new_named(
752 (),
753 "i_slint_core::Conditional::instance_generation",
754 ),
755 instance: RefCell::new(None),
756 }
757 }
758}
759
760impl<C: RepeatedItemTree + 'static> Conditional<C> {
761 pub fn track_model_changes(self: Pin<&Self>) {
764 self.project_ref().model.register_as_dependency();
765 }
766
767 pub fn track_instance_changes(self: Pin<&Self>) {
771 self.project_ref().instance_generation.register_as_dependency();
772 }
773
774 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) -> bool {
778 let model = self.project_ref().model.get();
779
780 let changed = if !model {
781 self.instance.take().is_some()
782 } else if self.instance.borrow().is_none() {
783 let i = init();
784 self.instance.replace(Some(i.clone()));
785 i.init();
786 true
787 } else {
788 false
789 };
790 if changed {
791 self.instance_generation.mark_dirty();
792 }
793 if let Some(instance) = self.instance.borrow().as_ref() {
794 crate::item_tree::ensure_item_tree_instantiated(&vtable::VRc::into_dyn(
795 instance.clone(),
796 )) || changed
797 } else {
798 changed
799 }
800 }
801
802 pub fn set_model_binding(&self, binding: impl Fn() -> bool + 'static) {
804 self.model.set_binding(binding);
805 }
806
807 pub fn visit(
811 self: Pin<&Self>,
812 order: TraversalOrder,
813 mut visitor: crate::item_tree::ItemVisitorRefMut,
814 ) -> crate::item_tree::VisitChildrenResult {
815 self.track_model_changes();
816 let instance = self.instance.borrow().clone();
818 if let Some(c) = instance
819 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
820 {
821 return crate::item_tree::VisitChildrenResult::abort(0, 0);
822 }
823
824 crate::item_tree::VisitChildrenResult::CONTINUE
825 }
826
827 pub fn len(&self) -> usize {
829 self.instance.borrow().is_some() as usize
830 }
831
832 pub fn range(&self) -> core::ops::Range<usize> {
836 0..self.len()
837 }
838
839 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
842 if index != 0 {
843 return None;
844 }
845 self.instance.borrow().clone()
846 }
847
848 pub fn is_empty(&self) -> bool {
850 self.len() == 0
851 }
852
853 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
855 self.instance.borrow().clone().into_iter().collect()
856 }
857}
858
859#[cfg(feature = "ffi")]
860mod ffi {
861 #![allow(unsafe_code)]
862
863 use super::*;
864
865 #[repr(C)]
868 pub struct RepeaterInstanceOpsVTable {
869 pub user_data: *mut core::ffi::c_void,
870 pub len: unsafe extern "C" fn(user_data: *mut core::ffi::c_void) -> usize,
871 pub splice: unsafe extern "C" fn(
872 user_data: *mut core::ffi::c_void,
873 position: usize,
874 remove: usize,
875 add: usize,
876 ),
877 pub ensure_updated: unsafe extern "C" fn(
878 user_data: *mut core::ffi::c_void,
879 instance_idx: usize,
880 row: usize,
881 ) -> bool,
882 pub height:
884 unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize) -> Coord,
885 pub listview_layout: Option<
886 unsafe extern "C" fn(
887 user_data: *mut core::ffi::c_void,
888 instance_idx: usize,
889 y: &mut Coord,
890 ) -> Coord,
891 >,
892 pub init: unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize),
893 }
894
895 impl RepeaterInstanceOps for RepeaterInstanceOpsVTable {
896 fn len(&self) -> usize {
897 unsafe { (self.len)(self.user_data) }
898 }
899 fn splice(&mut self, position: usize, remove: usize, add: usize) {
900 unsafe { (self.splice)(self.user_data, position, remove, add) }
901 }
902 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
903 let created = unsafe { (self.ensure_updated)(self.user_data, instance_idx, row) };
904 if created {
905 unsafe { (self.init)(self.user_data, instance_idx) };
906 }
907 created
908 }
909 fn height(&self, instance_idx: usize) -> Option<Coord> {
910 let h = unsafe { (self.height)(self.user_data, instance_idx) };
911 if h.is_nan() { None } else { Some(h) }
912 }
913 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
914 self.listview_layout
915 .map_or(0 as Coord, |f| unsafe { f(self.user_data, instance_idx, y) })
916 }
917 }
918
919 #[unsafe(no_mangle)]
920 pub extern "C" fn slint_repeater_ensure_updated(
921 ops: &mut RepeaterInstanceOpsVTable,
922 offset: usize,
923 count: usize,
924 ) {
925 update_all_instances(ops, offset, count);
926 }
927
928 #[unsafe(no_mangle)]
929 pub extern "C" fn slint_repeater_ensure_updated_listview(
930 ops: &mut RepeaterInstanceOpsVTable,
931 state: &mut RepeaterLayoutState,
932 row_count: usize,
933 viewport_width: Pin<&Property<LogicalLength>>,
934 viewport_height: Pin<&Property<LogicalLength>>,
935 viewport_y: Pin<&Property<LogicalLength>>,
936 listview_width: LogicalLength,
937 listview_height: LogicalLength,
938 ) -> bool {
939 update_visible_instances(
940 ops,
941 state,
942 row_count,
943 viewport_width,
944 viewport_height,
945 viewport_y,
946 listview_width,
947 listview_height,
948 )
949 }
950}