1use super::model_peer::{ModelChangeListener, ModelChangeListenerContainer};
13use super::{Model, 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(
146 ops: &mut impl RepeaterInstanceOps,
147 offset: usize,
148 count: usize,
149) -> Vec<usize> {
150 let cur = ops.len();
151 if count > cur {
152 ops.splice(cur, 0, count - cur);
153 } else if count < cur {
154 ops.splice(count, cur - count, 0);
155 }
156 let mut indices_to_init = Vec::new();
157 for i in 0..count {
158 if ops.ensure_updated(i, i + offset) {
159 indices_to_init.push(i);
160 }
161 }
162 indices_to_init
163}
164
165fn update_visible_instances(
171 ops: &mut impl RepeaterInstanceOps,
172 state: &mut RepeaterLayoutState,
173 row_count: usize,
174 viewport_width: Pin<&Property<LogicalLength>>,
175 viewport_height: Pin<&Property<LogicalLength>>,
176 viewport_y: Pin<&Property<LogicalLength>>,
177 listview_width: LogicalLength,
178 listview_height: LogicalLength,
179) -> Vec<usize> {
180 let zero = LogicalLength::default();
181 let mut vp_width = listview_width.get();
182 let listview_height = listview_height.get();
183
184 if row_count == 0 {
185 ops.splice(0, ops.len(), 0);
186 viewport_height.set(zero);
187 viewport_y.set(zero);
188 viewport_width.set(listview_width);
189 return Vec::new();
190 }
191
192 let mut vp_y = viewport_y.get().get();
193 if !viewport_y.has_binding() {
194 vp_y = vp_y.min(0 as Coord);
195 }
196
197 let mut indices_to_init = Vec::new();
198
199 let element_height = if state.cached_item_height > 0 as Coord {
201 state.cached_item_height
202 } else {
203 let mut total_height: Coord = 0 as Coord;
204 let mut count = 0usize;
205 for i in 0..ops.len() {
206 if let Some(h) = ops.height(i) {
207 total_height += h;
208 count += 1;
209 }
210 }
211
212 if count > 0 {
213 total_height / count as Coord
214 } else {
215 state.offset = state.offset.min(row_count - 1);
217 ops.splice(0, ops.len(), 1);
218 if ops.ensure_updated(0, state.offset) {
219 indices_to_init.push(0);
220 }
221 ops.height(0).unwrap_or(0 as Coord)
222 }
223 };
224
225 if state.offset >= row_count {
226 state.offset = row_count - 1;
227 }
228
229 let one_and_a_half_screen = listview_height * 3 as Coord / 2 as Coord;
230 let first_item_y = state.anchor_y;
231 let last_item_bottom = first_item_y + element_height * ops.len() as Coord;
232
233 let (mut new_offset, mut new_offset_y) = if first_item_y > -vp_y + one_and_a_half_screen
234 || last_item_bottom + element_height < -vp_y
235 {
236 ops.splice(0, ops.len(), 0);
238 state.offset = ((-vp_y / element_height).floor() as usize).min(row_count - 1);
239 (state.offset, 0 as Coord)
240 } else if vp_y < state.previous_viewport_y {
241 let mut it_y = first_item_y + vp_y;
243 let mut new_off = state.offset;
244 for i in 0..ops.len() {
245 if ops.ensure_updated(i, new_off) {
246 indices_to_init.push(i);
247 }
248 let h = ops.height(i).unwrap_or(0 as Coord);
249 if it_y + h > 0 as Coord || new_off + 1 >= row_count {
250 break;
251 }
252 it_y += h;
253 new_off += 1;
254 }
255 (new_off, it_y)
256 } else {
257 (state.offset, first_item_y + vp_y)
259 };
260
261 let mut loop_count = 0;
262 loop {
263 while new_offset > state.offset && new_offset_y > 0 as Coord {
265 new_offset -= 1;
266 new_offset_y -= ops.height(new_offset - state.offset).unwrap_or(0 as Coord);
267 }
268 let mut prepend_count = 0;
270 while new_offset > 0 && new_offset_y > 0 as Coord {
271 new_offset -= 1;
272 ops.splice(0, 0, 1);
273 ops.ensure_updated(0, new_offset);
274 new_offset_y -= ops.height(0).unwrap_or(0 as Coord);
275 prepend_count += 1;
276 }
277 if prepend_count > 0 {
278 for x in &mut indices_to_init {
279 *x += prepend_count;
280 }
281 indices_to_init.extend(0..prepend_count);
282 state.offset = new_offset;
283 }
284 debug_assert!(new_offset >= state.offset && new_offset <= state.offset + ops.len());
285
286 let mut y = new_offset_y;
288 let mut idx = new_offset;
289 let instances_begin = new_offset - state.offset;
290 for i in instances_begin..ops.len() {
291 if idx >= row_count {
292 break;
293 }
294 if ops.ensure_updated(i, idx) {
295 indices_to_init.push(i);
296 }
297 vp_width = vp_width.max(ops.listview_layout(i, &mut y));
298 idx += 1;
299 if y >= listview_height {
300 break;
301 }
302 }
303
304 while y < listview_height && idx < row_count {
306 let i = ops.len();
307 ops.splice(i, 0, 1);
308 ops.ensure_updated(i, idx);
309 indices_to_init.push(i);
310 vp_width = vp_width.max(ops.listview_layout(i, &mut y));
311 idx += 1;
312 }
313
314 if y < listview_height && vp_y < 0 as Coord && loop_count < 3 {
315 debug_assert!(idx >= row_count);
316 vp_y += listview_height - y;
318 loop_count += 1;
319 continue;
320 }
321
322 if new_offset != state.offset {
324 let remove_count = new_offset - state.offset;
325 ops.splice(0, remove_count, 0);
326 indices_to_init.retain_mut(|i| {
327 if *i < remove_count {
328 false
329 } else {
330 *i -= remove_count;
331 true
332 }
333 });
334 state.offset = new_offset;
335 }
336 let keep = idx - new_offset;
337 if ops.len() > keep {
338 ops.splice(keep, ops.len() - keep, 0);
339 indices_to_init.retain(|x| *x < keep);
340 }
341
342 if ops.len() == 0 {
343 break;
344 }
345
346 state.cached_item_height = (y - new_offset_y) / ops.len() as Coord;
348 state.anchor_y = state.cached_item_height * state.offset as Coord;
349 viewport_height.set(LogicalLength::new(state.cached_item_height * row_count as Coord));
350 viewport_width.set(LogicalLength::new(vp_width));
351 if !viewport_y.has_binding() {
353 let new_viewport_y = -state.anchor_y + new_offset_y;
354 if new_viewport_y != viewport_y.get().get() {
355 viewport_y.set(LogicalLength::new(new_viewport_y));
356 }
357 state.previous_viewport_y = new_viewport_y;
358 } else {
359 state.previous_viewport_y = viewport_y.get().0;
360 }
361 break;
362 }
363
364 indices_to_init
365}
366
367struct RustRepeaterOps<'a, C: RepeatedItemTree> {
369 instances: &'a mut Vec<(RepeatedInstanceState, Option<ItemTreeRc<C>>)>,
370 init: &'a dyn Fn() -> ItemTreeRc<C>,
371 model: &'a ModelRc<C::Data>,
372}
373
374impl<C: RepeatedItemTree> RepeaterInstanceOps for RustRepeaterOps<'_, C> {
375 fn len(&self) -> usize {
376 self.instances.len()
377 }
378
379 fn splice(&mut self, position: usize, remove: usize, add: usize) {
380 self.instances.splice(
381 position..position + remove,
382 core::iter::repeat_with(|| (RepeatedInstanceState::Dirty, None)).take(add),
383 );
384 }
385
386 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
387 let c = &mut self.instances[instance_idx];
388 if c.0 == RepeatedInstanceState::Dirty {
389 let created = c.1.is_none();
390 if created {
391 c.1 = Some((self.init)());
392 }
393 c.1.as_ref().unwrap().update(row, self.model.row_data(row).unwrap_or_default());
394 c.0 = RepeatedInstanceState::Clean;
395 created
396 } else {
397 false
398 }
399 }
400
401 fn height(&self, instance_idx: usize) -> Option<Coord> {
402 self.instances[instance_idx]
403 .1
404 .as_ref()
405 .map(|x| x.as_pin_ref().item_geometry(0).height_length().get())
406 }
407
408 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
409 let mut y_len = LogicalLength::new(*y);
410 let w = self.instances[instance_idx]
411 .1
412 .as_ref()
413 .unwrap()
414 .as_pin_ref()
415 .listview_layout(&mut y_len);
416 *y = y_len.get();
417 w.get()
418 }
419}
420
421#[pin_project]
424pub struct RepeaterTracker<T: RepeatedItemTree> {
425 inner: RefCell<RepeaterInner<T>>,
426 #[pin]
427 model: Property<ModelRc<T::Data>>,
428 #[pin]
429 is_dirty: Property<bool>,
431 #[pin]
433 listview_geometry_tracker: crate::properties::PropertyTracker,
434}
435
436impl<T: RepeatedItemTree> ModelChangeListener for RepeaterTracker<T> {
437 fn row_changed(self: Pin<&Self>, row: usize) {
439 let mut inner = self.inner.borrow_mut();
440 let inner = &mut *inner;
441 if let Some(c) = inner.instances.get_mut(row.wrapping_sub(inner.layout_state.offset)) {
442 if !self.model.is_dirty() {
443 if let Some(comp) = c.1.as_ref() {
444 let model = self.project_ref().model.get_untracked();
445 comp.update(row, model.row_data(row).unwrap_or_default());
446 c.0 = RepeatedInstanceState::Clean;
447 }
448 } else {
449 c.0 = RepeatedInstanceState::Dirty;
450 }
451 }
452 }
453 fn row_added(self: Pin<&Self>, mut index: usize, mut count: usize) {
455 let mut inner = self.inner.borrow_mut();
456 if index < inner.layout_state.offset {
457 if index + count <= inner.layout_state.offset {
458 inner.layout_state.offset += count;
460 self.is_dirty.set(true);
461 for c in inner.instances.iter_mut() {
462 c.0 = RepeatedInstanceState::Dirty;
463 }
464 return;
465 }
466 count -= inner.layout_state.offset - index;
467 index = 0;
468 } else {
469 index -= inner.layout_state.offset;
470 }
471 if count == 0 || index > inner.instances.len() {
472 return;
473 }
474 self.is_dirty.set(true);
475 inner.instances.splice(
476 index..index,
477 core::iter::repeat_n((RepeatedInstanceState::Dirty, None), count),
478 );
479 for c in inner.instances[index + count..].iter_mut() {
480 c.0 = RepeatedInstanceState::Dirty;
482 }
483 }
484 fn row_removed(self: Pin<&Self>, mut index: usize, mut count: usize) {
486 let mut inner = self.inner.borrow_mut();
487 if index < inner.layout_state.offset {
488 if index + count <= inner.layout_state.offset {
489 inner.layout_state.offset -= count;
491 self.is_dirty.set(true);
492 for c in inner.instances.iter_mut() {
493 c.0 = RepeatedInstanceState::Dirty;
494 }
495 return;
496 }
497 count -= inner.layout_state.offset - index;
498 inner.layout_state.offset = index;
499 index = 0;
500 } else {
501 index -= inner.layout_state.offset;
502 }
503 if count == 0 || index >= inner.instances.len() {
504 return;
505 }
506 if (index + count) > inner.instances.len() {
507 count = inner.instances.len() - index;
508 }
509 self.is_dirty.set(true);
510 inner.instances.drain(index..(index + count));
511 for c in inner.instances[index..].iter_mut() {
512 c.0 = RepeatedInstanceState::Dirty;
514 }
515 }
516
517 fn reset(self: Pin<&Self>) {
518 self.is_dirty.set(true);
519 self.inner.borrow_mut().instances.clear();
520 }
521}
522
523impl<C: RepeatedItemTree> Default for RepeaterTracker<C> {
524 fn default() -> Self {
525 Self {
526 inner: Default::default(),
527 model: Property::new_named(ModelRc::default(), "i_slint_core::Repeater::model"),
528 is_dirty: Property::new_named(false, "i_slint_core::Repeater::is_dirty"),
529 listview_geometry_tracker: Default::default(),
530 }
531 }
532}
533
534#[pin_project]
535pub struct Repeater<C: RepeatedItemTree>(#[pin] ModelChangeListenerContainer<RepeaterTracker<C>>);
536
537impl<C: RepeatedItemTree> Default for Repeater<C> {
538 fn default() -> Self {
539 Self(Default::default())
540 }
541}
542
543impl<C: RepeatedItemTree + 'static> Repeater<C> {
544 fn data(self: Pin<&Self>) -> Pin<&RepeaterTracker<C>> {
545 self.project_ref().0.get()
546 }
547
548 fn model(self: Pin<&Self>) -> ModelRc<C::Data> {
549 let model = self.data().project_ref().model;
550
551 if model.is_dirty() {
552 let old_model = model.get_internal();
553 let m = model.get();
554 if old_model != m {
555 *self.data().inner.borrow_mut() = RepeaterInner::default();
556 self.data().is_dirty.set(true);
557 let peer = self.project_ref().0.model_peer();
558 m.model_tracker().attach_peer(peer);
559 }
560 m
561 } else {
562 model.get()
563 }
564 }
565
566 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) {
569 let model = self.model();
570 if !self.data().project_ref().is_dirty.get() {
571 return;
572 }
573 let count = model.row_count();
574 let mut inner = self.0.inner.borrow_mut();
575 let offset = inner.layout_state.offset;
576 let mut ops =
577 RustRepeaterOps { instances: &mut inner.instances, init: &init, model: &model };
578 self.data().is_dirty.set(false);
579 let indices_to_init = update_all_instances(&mut ops, offset, count);
580
581 drop(inner);
582 self.init_instances(indices_to_init);
583 }
584
585 fn init_instances(&self, indices: Vec<usize>) {
586 let inner = self.0.inner.borrow();
587 for index in indices {
588 if let Some((_, Some(comp))) = inner.instances.get(index) {
589 comp.init();
590 }
591 }
592 }
593
594 pub fn ensure_updated_listview(
596 self: Pin<&Self>,
597 init: impl Fn() -> ItemTreeRc<C>,
598 viewport_width: Pin<&Property<LogicalLength>>,
599 viewport_height: Pin<&Property<LogicalLength>>,
600 viewport_y: Pin<&Property<LogicalLength>>,
601 listview_width: LogicalLength,
602 listview_height: Pin<&Property<LogicalLength>>,
603 ) {
604 let _ = self.data().project_ref().is_dirty.get();
606 self.data().project_ref().is_dirty.set(false);
607
608 let model = self.model();
609 let row_count = model.row_count();
610
611 let data = self.data();
612 let mut inner = data.inner.borrow_mut();
613
614 let RepeaterInner { ref mut instances, ref mut layout_state } = *inner;
615 let mut ops = RustRepeaterOps { instances, init: &init, model: &model };
616 let indices_to_init = update_visible_instances(
617 &mut ops,
618 layout_state,
619 row_count,
620 viewport_width,
621 viewport_height,
622 viewport_y,
623 listview_width,
624 listview_height.get(),
625 );
626
627 drop(inner);
628 self.init_instances(indices_to_init);
629 }
630
631 pub fn model_set_row_data(self: Pin<&Self>, row: usize, data: C::Data) {
633 let model = self.model();
634 model.set_row_data(row, data);
635 }
636
637 pub fn set_model_binding(&self, binding: impl Fn() -> ModelRc<C::Data> + 'static) {
639 self.0.model.set_binding(binding);
640 }
641
642 pub fn visit(
644 &self,
645 order: TraversalOrder,
646 mut visitor: crate::item_tree::ItemVisitorRefMut,
647 ) -> crate::item_tree::VisitChildrenResult {
648 let count = self.0.inner.borrow().instances.len() as u32;
650 for i in 0..count {
651 let i = if order == TraversalOrder::BackToFront { i } else { count - i - 1 };
652 let c = self.0.inner.borrow().instances.get(i as usize).and_then(|c| c.1.clone());
653 if let Some(c) = c
654 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
655 {
656 return crate::item_tree::VisitChildrenResult::abort(i, 0);
657 }
658 }
659 crate::item_tree::VisitChildrenResult::CONTINUE
660 }
661
662 pub fn len(&self) -> usize {
664 self.0.inner.borrow().instances.len()
665 }
666
667 pub fn range(&self) -> core::ops::Range<usize> {
672 let inner = self.0.inner.borrow();
673 core::ops::Range {
674 start: inner.layout_state.offset,
675 end: inner.layout_state.offset + inner.instances.len(),
676 }
677 }
678
679 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
682 let inner = self.0.inner.borrow();
683 inner
684 .instances
685 .get(index.checked_sub(inner.layout_state.offset)?)
686 .map(|c| c.1.clone().expect("That was updated before!"))
687 }
688
689 pub fn is_empty(&self) -> bool {
691 self.len() == 0
692 }
693
694 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
696 self.0.inner.borrow().instances.iter().flat_map(|x| x.1.clone()).collect()
697 }
698}
699
700#[pin_project]
701pub struct Conditional<C: RepeatedItemTree> {
702 #[pin]
703 model: Property<bool>,
704 instance: RefCell<Option<ItemTreeRc<C>>>,
705}
706
707impl<C: RepeatedItemTree> Default for Conditional<C> {
708 fn default() -> Self {
709 Self {
710 model: Property::new_named(false, "i_slint_core::Conditional::model"),
711 instance: RefCell::new(None),
712 }
713 }
714}
715
716impl<C: RepeatedItemTree + 'static> Conditional<C> {
717 pub fn ensure_updated(self: Pin<&Self>, init: impl Fn() -> ItemTreeRc<C>) {
720 let model = self.project_ref().model.get();
721
722 if !model {
723 drop(self.instance.replace(None));
724 } else if self.instance.borrow().is_none() {
725 let i = init();
726 self.instance.replace(Some(i.clone()));
727 i.init();
728 }
729 }
730
731 pub fn set_model_binding(&self, binding: impl Fn() -> bool + 'static) {
733 self.model.set_binding(binding);
734 }
735
736 pub fn visit(
738 &self,
739 order: TraversalOrder,
740 mut visitor: crate::item_tree::ItemVisitorRefMut,
741 ) -> crate::item_tree::VisitChildrenResult {
742 let instance = self.instance.borrow().clone();
744 if let Some(c) = instance
745 && c.as_pin_ref().visit_children_item(-1, order, visitor.borrow_mut()).has_aborted()
746 {
747 return crate::item_tree::VisitChildrenResult::abort(0, 0);
748 }
749
750 crate::item_tree::VisitChildrenResult::CONTINUE
751 }
752
753 pub fn len(&self) -> usize {
755 self.instance.borrow().is_some() as usize
756 }
757
758 pub fn range(&self) -> core::ops::Range<usize> {
762 0..self.len()
763 }
764
765 pub fn instance_at(&self, index: usize) -> Option<ItemTreeRc<C>> {
768 if index != 0 {
769 return None;
770 }
771 self.instance.borrow().clone()
772 }
773
774 pub fn is_empty(&self) -> bool {
776 self.len() == 0
777 }
778
779 pub fn instances_vec(&self) -> Vec<ItemTreeRc<C>> {
781 self.instance.borrow().clone().into_iter().collect()
782 }
783}
784
785#[cfg(feature = "ffi")]
786mod ffi {
787 #![allow(unsafe_code)]
788
789 use super::*;
790
791 #[repr(C)]
794 pub struct RepeaterInstanceOpsVTable {
795 pub user_data: *mut core::ffi::c_void,
796 pub len: unsafe extern "C" fn(user_data: *mut core::ffi::c_void) -> usize,
797 pub splice: unsafe extern "C" fn(
798 user_data: *mut core::ffi::c_void,
799 position: usize,
800 remove: usize,
801 add: usize,
802 ),
803 pub ensure_updated: unsafe extern "C" fn(
804 user_data: *mut core::ffi::c_void,
805 instance_idx: usize,
806 row: usize,
807 ) -> bool,
808 pub height:
810 unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize) -> Coord,
811 pub listview_layout: Option<
812 unsafe extern "C" fn(
813 user_data: *mut core::ffi::c_void,
814 instance_idx: usize,
815 y: &mut Coord,
816 ) -> Coord,
817 >,
818 pub init: unsafe extern "C" fn(user_data: *mut core::ffi::c_void, instance_idx: usize),
819 }
820
821 impl RepeaterInstanceOpsVTable {
822 fn init_instances(&self, indices: Vec<usize>) {
823 for idx in indices {
824 unsafe { (self.init)(self.user_data, idx) };
825 }
826 }
827 }
828
829 impl RepeaterInstanceOps for RepeaterInstanceOpsVTable {
830 fn len(&self) -> usize {
831 unsafe { (self.len)(self.user_data) }
832 }
833 fn splice(&mut self, position: usize, remove: usize, add: usize) {
834 unsafe { (self.splice)(self.user_data, position, remove, add) }
835 }
836 fn ensure_updated(&mut self, instance_idx: usize, row: usize) -> bool {
837 unsafe { (self.ensure_updated)(self.user_data, instance_idx, row) }
838 }
839 fn height(&self, instance_idx: usize) -> Option<Coord> {
840 let h = unsafe { (self.height)(self.user_data, instance_idx) };
841 if h.is_nan() { None } else { Some(h) }
842 }
843 fn listview_layout(&self, instance_idx: usize, y: &mut Coord) -> Coord {
844 self.listview_layout
845 .map_or(0 as Coord, |f| unsafe { f(self.user_data, instance_idx, y) })
846 }
847 }
848
849 #[unsafe(no_mangle)]
850 pub extern "C" fn slint_repeater_ensure_updated(
851 ops: &mut RepeaterInstanceOpsVTable,
852 offset: usize,
853 count: usize,
854 ) {
855 let indices_to_init = update_all_instances(ops, offset, count);
856 ops.init_instances(indices_to_init);
857 }
858
859 #[unsafe(no_mangle)]
860 pub extern "C" fn slint_repeater_ensure_updated_listview(
861 ops: &mut RepeaterInstanceOpsVTable,
862 state: &mut RepeaterLayoutState,
863 row_count: usize,
864 viewport_width: Pin<&Property<LogicalLength>>,
865 viewport_height: Pin<&Property<LogicalLength>>,
866 viewport_y: Pin<&Property<LogicalLength>>,
867 listview_width: LogicalLength,
868 listview_height: LogicalLength,
869 ) {
870 let indices_to_init = update_visible_instances(
871 ops,
872 state,
873 row_count,
874 viewport_width,
875 viewport_height,
876 viewport_y,
877 listview_width,
878 listview_height,
879 );
880 ops.init_instances(indices_to_init);
881 }
882}