1use alloc::sync::Arc;
2use alloc::vec::Vec;
3use core::cell::Cell;
4use core::cmp;
5
6use crate::fenwick::Fenwick;
7use crate::key::{KeyCacheKey, KeySizeMap};
8use crate::{
9 Align, InitialOffset, ItemKey, Range, Rect, ScrollDirection, VirtualItem, VirtualItemKeyed,
10 VirtualRange, VirtualizerOptions,
11};
12use crate::{FrameState, ScrollState, ViewportState};
13
14#[derive(Clone, Debug)]
23pub struct Virtualizer<K = ItemKey> {
24 options: VirtualizerOptions<K>,
25 viewport_size: u32,
26 scroll_offset: u64,
27 scroll_rect: Rect,
28 is_scrolling: bool,
29 scroll_direction: Option<ScrollDirection>,
30 last_scroll_event_ms: Option<u64>,
31
32 sizes: Vec<u32>, measured: Vec<bool>,
34 sums: Fenwick,
35 key_sizes: KeySizeMap<K>,
36
37 notify_depth: Cell<usize>,
38 notify_pending: Cell<bool>,
39}
40
41impl<K: KeyCacheKey> Virtualizer<K> {
42 pub fn new(options: VirtualizerOptions<K>) -> Self {
47 let scroll_rect = options.initial_rect.unwrap_or_default();
48 let scroll_offset = options.initial_offset.resolve();
49 vdebug!(
50 count = options.count,
51 enabled = options.enabled,
52 overscan = options.overscan,
53 "Virtualizer::new"
54 );
55 let mut v = Self {
56 viewport_size: scroll_rect.main,
57 scroll_offset,
58 scroll_rect,
59 is_scrolling: false,
60 scroll_direction: None,
61 last_scroll_event_ms: None,
62 sizes: Vec::new(),
63 measured: Vec::new(),
64 sums: Fenwick::new(0),
65 key_sizes: KeySizeMap::<K>::new(),
66 options,
67 notify_depth: Cell::new(0),
68 notify_pending: Cell::new(false),
69 };
70 v.rebuild_estimates();
71 v
72 }
73
74 pub fn options(&self) -> &VirtualizerOptions<K> {
75 &self.options
76 }
77
78 fn reset_to_initial(&mut self) {
79 self.scroll_offset = self.options.initial_offset.resolve();
80 self.scroll_rect = self.options.initial_rect.unwrap_or_default();
81 self.viewport_size = self.scroll_rect.main;
82 self.is_scrolling = false;
83 self.scroll_direction = None;
84 self.last_scroll_event_ms = None;
85 }
86
87 pub fn set_options(&mut self, options: VirtualizerOptions<K>) {
88 let prev_count = self.options.count;
89 let prev_gap = self.options.gap;
90 let was_enabled = self.options.enabled;
91 let estimate_size_unchanged =
92 Arc::ptr_eq(&self.options.estimate_size, &options.estimate_size);
93 let get_item_key_unchanged = Arc::ptr_eq(&self.options.get_item_key, &options.get_item_key);
94 self.options = options;
95 vtrace!(
96 count = self.options.count,
97 enabled = self.options.enabled,
98 overscan = self.options.overscan,
99 "Virtualizer::set_options"
100 );
101
102 if !self.options.enabled {
103 self.viewport_size = 0;
104 self.scroll_offset = self.options.initial_offset.resolve();
105 self.scroll_rect = Rect::default();
106 self.is_scrolling = false;
107 self.scroll_direction = None;
108 self.last_scroll_event_ms = None;
109 } else if !was_enabled {
110 self.reset_to_initial();
111 } else if self.options.count != prev_count {
112 if estimate_size_unchanged && get_item_key_unchanged {
113 self.resize_count(prev_count, self.options.count);
114 } else {
115 self.rebuild_estimates();
116 }
117 } else if !estimate_size_unchanged || !get_item_key_unchanged {
118 self.rebuild_estimates();
119 } else if self.options.gap != prev_gap {
120 self.rebuild_fenwick();
121 }
122
123 self.notify();
124 }
125
126 pub fn update_options(&mut self, f: impl FnOnce(&mut VirtualizerOptions<K>)) {
131 let mut next = self.options.clone();
132 f(&mut next);
133 self.set_options(next);
134 }
135
136 pub fn set_on_change(
137 &mut self,
138 on_change: Option<impl Fn(&Virtualizer<K>, bool) + Send + Sync + 'static>,
139 ) {
140 self.options.on_change = on_change.map(|f| Arc::new(f) as _);
141 self.notify();
142 }
143
144 pub fn set_initial_offset(&mut self, initial_offset: u64) {
145 self.options.initial_offset = InitialOffset::Value(initial_offset);
146 self.notify();
147 }
148
149 pub fn set_initial_offset_provider(
150 &mut self,
151 initial_offset: impl Fn() -> u64 + Send + Sync + 'static,
152 ) {
153 self.options.initial_offset = InitialOffset::Provider(Arc::new(initial_offset));
154 self.notify();
155 }
156
157 pub fn set_use_scrollend_event(&mut self, use_scrollend_event: bool) {
158 self.options.use_scrollend_event = use_scrollend_event;
159 self.notify();
160 }
161
162 pub fn set_is_scrolling_reset_delay_ms(&mut self, delay_ms: u64) {
163 self.options.is_scrolling_reset_delay_ms = delay_ms;
164 self.notify();
165 }
166
167 fn notify_now(&self) {
168 if let Some(cb) = &self.options.on_change {
169 cb(self, self.is_scrolling);
170 }
171 }
172
173 fn notify(&self) {
174 if self.notify_depth.get() > 0 {
175 self.notify_pending.set(true);
176 return;
177 }
178 self.notify_now();
179 }
180
181 pub fn batch_update(&mut self, f: impl FnOnce(&mut Self)) {
187 let depth = self.notify_depth.get();
188 self.notify_depth.set(depth.saturating_add(1));
189
190 f(self);
191
192 let depth = self.notify_depth.get();
193 debug_assert!(depth > 0, "notify_depth underflow");
194 let next = depth.saturating_sub(1);
195 self.notify_depth.set(next);
196
197 if next == 0 && self.notify_pending.replace(false) {
198 self.notify_now();
199 }
200 }
201
202 pub fn count(&self) -> usize {
203 self.options.count
204 }
205
206 pub fn enabled(&self) -> bool {
207 self.options.enabled
208 }
209
210 pub fn set_enabled(&mut self, enabled: bool) {
211 if self.options.enabled == enabled {
212 return;
213 }
214 self.options.enabled = enabled;
215 if !enabled {
216 self.viewport_size = 0;
217 self.scroll_offset = self.options.initial_offset.resolve();
218 self.scroll_rect = Rect::default();
219 self.is_scrolling = false;
220 self.scroll_direction = None;
221 self.last_scroll_event_ms = None;
222 } else {
223 self.reset_to_initial();
224 }
225 self.notify();
226 }
227
228 pub fn is_scrolling(&self) -> bool {
229 self.is_scrolling
230 }
231
232 pub fn scroll_direction(&self) -> Option<ScrollDirection> {
233 self.scroll_direction
234 }
235
236 pub fn set_is_scrolling(&mut self, is_scrolling: bool) {
237 if self.is_scrolling == is_scrolling {
238 return;
239 }
240 self.is_scrolling = is_scrolling;
241 if !is_scrolling {
242 self.scroll_direction = None;
243 self.last_scroll_event_ms = None;
244 }
245 self.notify();
246 }
247
248 pub fn notify_scroll_event(&mut self, now_ms: u64) {
249 if !self.options.enabled {
250 return;
251 }
252 self.last_scroll_event_ms = Some(now_ms);
253 self.set_is_scrolling(true);
254 }
255
256 pub fn update_scrolling(&mut self, now_ms: u64) {
257 if !self.options.enabled {
258 return;
259 }
260 if self.options.use_scrollend_event {
261 return;
262 }
263 if !self.is_scrolling {
264 return;
265 }
266 let Some(last) = self.last_scroll_event_ms else {
267 return;
268 };
269 if now_ms.saturating_sub(last) >= self.options.is_scrolling_reset_delay_ms {
270 self.set_is_scrolling(false);
271 }
272 }
273
274 pub fn viewport_size(&self) -> u32 {
275 self.viewport_size
276 }
277
278 pub fn scroll_rect(&self) -> Rect {
279 self.scroll_rect
280 }
281
282 pub fn viewport_state(&self) -> ViewportState {
284 ViewportState {
285 rect: self.scroll_rect,
286 }
287 }
288
289 pub fn scroll_state(&self) -> ScrollState {
291 ScrollState {
292 offset: self.scroll_offset,
293 is_scrolling: self.is_scrolling,
294 }
295 }
296
297 pub fn frame_state(&self) -> FrameState {
299 FrameState {
300 viewport: self.viewport_state(),
301 scroll: self.scroll_state(),
302 }
303 }
304
305 pub fn restore_viewport_state(&mut self, viewport: ViewportState) {
307 self.set_scroll_rect(viewport.rect);
308 }
309
310 pub fn restore_scroll_state(&mut self, scroll: ScrollState, now_ms: u64) {
315 if scroll.is_scrolling {
316 self.apply_scroll_offset_event_clamped(scroll.offset, now_ms);
317 return;
318 }
319 self.batch_update(|v| {
320 v.set_scroll_offset_clamped(scroll.offset);
321 v.set_is_scrolling(false);
322 });
323 }
324
325 pub fn restore_frame_state(&mut self, frame: FrameState, now_ms: u64) {
330 if frame.scroll.is_scrolling {
331 self.apply_scroll_frame_clamped(frame.viewport.rect, frame.scroll.offset, now_ms);
332 return;
333 }
334 self.batch_update(|v| {
335 v.set_scroll_rect(frame.viewport.rect);
336 v.set_scroll_offset_clamped(frame.scroll.offset);
337 v.set_is_scrolling(false);
338 });
339 }
340
341 pub fn set_scroll_rect(&mut self, rect: Rect) {
342 if self.scroll_rect == rect {
343 return;
344 }
345 self.scroll_rect = rect;
346 self.viewport_size = rect.main;
347 self.notify();
348 }
349
350 pub fn apply_scroll_rect_event(&mut self, rect: Rect) {
355 self.batch_update(|v| {
356 v.set_scroll_rect(rect);
357 });
358 }
359
360 pub fn scroll_offset(&self) -> u64 {
361 self.scroll_offset
362 }
363
364 pub fn scroll_offset_in_list(&self) -> u64 {
365 let margin = self.options.scroll_margin as u64;
366 self.scroll_offset.saturating_sub(margin)
367 }
368
369 pub fn set_viewport_size(&mut self, size: u32) {
370 if self.viewport_size == size && self.scroll_rect.main == size {
371 return;
372 }
373 self.viewport_size = size;
374 self.scroll_rect.main = size;
375 self.notify();
376 }
377
378 pub fn set_scroll_offset(&mut self, offset: u64) {
379 if self.scroll_offset == offset {
380 return;
381 }
382 let prev = self.scroll_offset;
383 self.scroll_offset = offset;
384 self.scroll_direction = match offset.cmp(&prev) {
385 cmp::Ordering::Greater => Some(ScrollDirection::Forward),
386 cmp::Ordering::Less => Some(ScrollDirection::Backward),
387 cmp::Ordering::Equal => self.scroll_direction,
388 };
389 self.notify();
390 }
391
392 pub fn apply_scroll_offset_event(&mut self, offset: u64, now_ms: u64) {
395 vtrace!(offset, now_ms, "apply_scroll_offset_event");
396 self.batch_update(|v| {
397 v.set_scroll_offset(offset);
398 v.notify_scroll_event(now_ms);
399 });
400 }
401
402 pub fn set_scroll_offset_clamped(&mut self, offset: u64) {
403 let clamped = self.clamp_scroll_offset(offset);
404 self.set_scroll_offset(clamped);
405 }
406
407 pub fn apply_scroll_offset_event_clamped(&mut self, offset: u64, now_ms: u64) {
409 vtrace!(offset, now_ms, "apply_scroll_offset_event_clamped");
410 self.batch_update(|v| {
411 v.set_scroll_offset_clamped(offset);
412 v.notify_scroll_event(now_ms);
413 });
414 }
415
416 pub fn set_viewport_and_scroll(&mut self, viewport_size: u32, scroll_offset: u64) {
417 self.batch_update(|v| {
418 v.set_viewport_size(viewport_size);
419 v.set_scroll_offset(scroll_offset);
420 });
421 }
422
423 pub fn set_viewport_and_scroll_clamped(&mut self, viewport_size: u32, scroll_offset: u64) {
424 self.batch_update(|v| {
425 v.set_viewport_size(viewport_size);
426 v.set_scroll_offset_clamped(scroll_offset);
427 });
428 }
429
430 pub fn apply_scroll_frame(&mut self, rect: Rect, scroll_offset: u64, now_ms: u64) {
435 vtrace!(
436 rect_main = rect.main,
437 rect_cross = rect.cross,
438 scroll_offset,
439 now_ms,
440 "apply_scroll_frame"
441 );
442 self.batch_update(|v| {
443 v.set_scroll_rect(rect);
444 v.set_scroll_offset(scroll_offset);
445 v.notify_scroll_event(now_ms);
446 });
447 }
448
449 pub fn apply_scroll_frame_clamped(&mut self, rect: Rect, scroll_offset: u64, now_ms: u64) {
451 vtrace!(
452 rect_main = rect.main,
453 rect_cross = rect.cross,
454 scroll_offset,
455 now_ms,
456 "apply_scroll_frame_clamped"
457 );
458 self.batch_update(|v| {
459 v.set_scroll_rect(rect);
460 v.set_scroll_offset_clamped(scroll_offset);
461 v.notify_scroll_event(now_ms);
462 });
463 }
464
465 pub fn set_count(&mut self, count: usize) {
466 if self.options.count == count {
467 return;
468 }
469 let prev = self.options.count;
470 self.options.count = count;
471 self.resize_count(prev, count);
472 self.notify();
473 }
474
475 pub fn set_overscan(&mut self, overscan: usize) {
476 self.options.overscan = overscan;
477 self.notify();
478 }
479
480 pub fn set_padding(&mut self, padding_start: u32, padding_end: u32) {
481 self.options.padding_start = padding_start;
482 self.options.padding_end = padding_end;
483 self.notify();
484 }
485
486 pub fn set_scroll_padding(&mut self, scroll_padding_start: u32, scroll_padding_end: u32) {
487 self.options.scroll_padding_start = scroll_padding_start;
488 self.options.scroll_padding_end = scroll_padding_end;
489 self.notify();
490 }
491
492 pub fn set_scroll_margin(&mut self, scroll_margin: u32) {
493 self.options.scroll_margin = scroll_margin;
494 self.notify();
495 }
496
497 pub fn set_gap(&mut self, gap: u32) {
498 if self.options.gap == gap {
499 return;
500 }
501 self.options.gap = gap;
502 self.rebuild_fenwick();
503 self.notify();
504 }
505
506 pub fn set_get_item_key(&mut self, f: impl Fn(usize) -> K + Send + Sync + 'static) {
507 self.options.get_item_key = Arc::new(f);
508 self.rebuild_estimates();
509 self.notify();
510 }
511
512 pub fn set_should_adjust_scroll_position_on_item_size_change(
513 &mut self,
514 f: Option<impl Fn(&Virtualizer<K>, VirtualItem, i64) -> bool + Send + Sync + 'static>,
515 ) {
516 self.options
517 .should_adjust_scroll_position_on_item_size_change = f.map(|f| Arc::new(f) as _);
518 self.notify();
519 }
520
521 pub fn sync_item_keys(&mut self) {
522 let count = self.options.count;
525 self.sizes.clear();
526 self.measured.clear();
527 self.sizes.reserve_exact(count);
528 self.measured.reserve_exact(count);
529
530 for i in 0..count {
531 let key = self.key_for(i);
532 if let Some(&measured_size) = self.key_sizes.get(&key) {
533 self.sizes.push(measured_size);
534 self.measured.push(true);
535 } else {
536 self.sizes.push((self.options.estimate_size)(i));
537 self.measured.push(false);
538 }
539 }
540
541 self.rebuild_fenwick();
542 self.notify();
543 }
544
545 pub fn set_range_extractor(
546 &mut self,
547 f: Option<impl Fn(Range, &mut dyn FnMut(usize)) + Send + Sync + 'static>,
548 ) {
549 self.options.range_extractor = f.map(|f| Arc::new(f) as _);
550 self.notify();
551 }
552
553 pub fn set_estimate_size(&mut self, f: impl Fn(usize) -> u32 + Send + Sync + 'static) {
554 self.options.estimate_size = Arc::new(f);
555 self.rebuild_estimates();
556 self.notify();
557 }
558
559 pub fn reset_measurements(&mut self) {
560 self.key_sizes.clear();
561 self.rebuild_estimates();
562 self.notify();
563 }
564
565 pub fn measurement_cache_len(&self) -> usize {
567 self.key_sizes.len()
568 }
569
570 pub fn for_each_cached_size(&self, mut f: impl FnMut(&K, u32)) {
572 for (k, v) in self.key_sizes.iter() {
573 f(k, *v);
574 }
575 }
576
577 pub fn export_measurement_cache(&self) -> Vec<(K, u32)>
579 where
580 K: Clone,
581 {
582 let mut out = Vec::with_capacity(self.key_sizes.len());
583 self.for_each_cached_size(|k, v| out.push((k.clone(), v)));
584 out
585 }
586
587 pub fn import_measurement_cache(&mut self, entries: impl IntoIterator<Item = (K, u32)>) {
591 self.key_sizes.clear();
592 let mut n = 0usize;
593 for (k, v) in entries {
594 self.key_sizes.insert(k, v);
595 n = n.saturating_add(1);
596 }
597 vdebug!(entries = n, "import_measurement_cache");
598 self.rebuild_estimates();
599 self.notify();
600 }
601
602 pub fn measure(&mut self, index: usize, size: u32) {
611 if index >= self.options.count {
612 return;
613 }
614 let _ = self.resize_item(index, size);
615 }
616
617 pub fn measure_keyed(&mut self, index: usize, key: K, size: u32) {
619 if index >= self.options.count {
620 return;
621 }
622 let _ = self.resize_item_keyed(index, key, size);
623 }
624
625 pub fn measure_unadjusted(&mut self, index: usize, size: u32) {
627 if index >= self.options.count {
628 return;
629 }
630 let key = self.key_for(index);
631 self.measure_keyed_unadjusted(index, key, size);
632 }
633
634 pub fn measure_keyed_unadjusted(&mut self, index: usize, key: K, size: u32) {
636 if index >= self.options.count {
637 return;
638 }
639 vtrace!(index, size, "measure_keyed_unadjusted");
640 self.set_item_size_keyed(index, key, size);
641 self.notify();
642 }
643
644 pub fn resize_item(&mut self, index: usize, size: u32) -> i64 {
645 if index >= self.options.count {
646 return 0;
647 }
648 let key = self.key_for(index);
649 self.resize_item_keyed(index, key, size)
650 }
651
652 pub fn resize_item_keyed(&mut self, index: usize, key: K, size: u32) -> i64 {
653 if index >= self.options.count {
654 return 0;
655 }
656 let item = self.item(index);
657 let delta = self.set_item_size_keyed(index, key, size);
658 if delta == 0 {
659 self.notify();
660 return 0;
661 }
662
663 let should_adjust = if let Some(f) = &self
664 .options
665 .should_adjust_scroll_position_on_item_size_change
666 {
667 f(self, item, delta)
668 } else {
669 item.start < self.scroll_offset
670 };
671
672 if should_adjust {
673 if delta > 0 {
674 self.scroll_offset = self.scroll_offset.saturating_add(delta as u64);
675 } else {
676 self.scroll_offset = self.scroll_offset.saturating_sub((-delta) as u64);
677 }
678 self.notify();
679 delta
680 } else {
681 self.notify();
682 0
683 }
684 }
685
686 fn set_item_size_keyed(&mut self, index: usize, key: K, size: u32) -> i64 {
687 let cur = self.sizes[index];
688 if cur == size {
689 self.measured[index] = true;
690 self.key_sizes.insert(key, size);
691 return 0;
692 }
693 self.sizes[index] = size;
694 self.measured[index] = true;
695 self.key_sizes.insert(key, size);
696 let delta = size as i64 - cur as i64;
697 self.sums.add(index, delta);
698 delta
699 }
700
701 pub fn measure_many(&mut self, measurements: impl IntoIterator<Item = (usize, u32)>) {
705 let _ = self.resize_item_many(measurements);
706 }
707
708 pub fn measure_many_unadjusted(
710 &mut self,
711 measurements: impl IntoIterator<Item = (usize, u32)>,
712 ) {
713 for (index, size) in measurements {
714 if index >= self.options.count {
715 continue;
716 }
717 let key = self.key_for(index);
718 let cur = self.sizes[index];
719 if cur == size {
720 self.measured[index] = true;
721 self.key_sizes.insert(key, size);
722 continue;
723 }
724 self.sizes[index] = size;
725 self.measured[index] = true;
726 self.key_sizes.insert(key, size);
727 self.sums.add(index, size as i64 - cur as i64);
728 }
729 self.notify();
730 }
731
732 pub fn resize_item_many(
733 &mut self,
734 measurements: impl IntoIterator<Item = (usize, u32)>,
735 ) -> i64 {
736 let mut applied = 0i64;
737 self.batch_update(|v| {
738 for (index, size) in measurements {
739 if index >= v.options.count {
740 continue;
741 }
742 applied += v.resize_item(index, size);
743 }
744 });
745 applied
746 }
747
748 pub fn is_measured(&self, index: usize) -> bool {
749 self.measured.get(index).copied().unwrap_or(false)
750 }
751
752 pub fn total_size(&self) -> u64 {
753 if !self.options.enabled {
754 return 0;
755 }
756 self.options.padding_start as u64 + self.sums.total() + self.options.padding_end as u64
757 }
758
759 pub fn key_for(&self, index: usize) -> K {
760 (self.options.get_item_key)(index)
761 }
762
763 pub fn virtual_range(&self) -> VirtualRange {
764 if !self.options.enabled {
765 return VirtualRange {
766 start_index: 0,
767 end_index: 0,
768 };
769 }
770 self.compute_range(self.scroll_offset, self.viewport_size)
771 }
772
773 pub fn virtual_range_for(&self, scroll_offset: u64, viewport_size: u32) -> VirtualRange {
774 if !self.options.enabled {
775 return VirtualRange {
776 start_index: 0,
777 end_index: 0,
778 };
779 }
780 self.compute_range(scroll_offset, viewport_size)
781 }
782
783 pub fn visible_range(&self) -> VirtualRange {
784 if !self.options.enabled {
785 return VirtualRange {
786 start_index: 0,
787 end_index: 0,
788 };
789 }
790 self.compute_visible_range(self.scroll_offset, self.viewport_size)
791 }
792
793 pub fn visible_range_for(&self, scroll_offset: u64, viewport_size: u32) -> VirtualRange {
794 if !self.options.enabled {
795 return VirtualRange {
796 start_index: 0,
797 end_index: 0,
798 };
799 }
800 self.compute_visible_range(scroll_offset, viewport_size)
801 }
802
803 pub fn for_each_virtual_index(&self, f: impl FnMut(usize)) {
804 self.for_each_virtual_index_for(self.scroll_offset, self.viewport_size, f);
805 }
806
807 pub fn for_each_virtual_index_for(
808 &self,
809 scroll_offset: u64,
810 viewport_size: u32,
811 mut f: impl FnMut(usize),
812 ) {
813 if !self.options.enabled {
814 return;
815 }
816
817 let visible = self.visible_range_for(scroll_offset, viewport_size);
818 if visible.is_empty() {
819 return;
820 }
821
822 let count = self.options.count;
823 let range = Range {
824 start_index: visible.start_index,
825 end_index: visible.end_index,
826 overscan: self.options.overscan,
827 count,
828 };
829
830 if let Some(extract) = &self.options.range_extractor {
831 let mut prev: Option<usize> = None;
832 extract(range, &mut |i| {
833 if i >= count {
834 debug_assert!(
835 i < count,
836 "range_extractor emitted out-of-bounds index (i={i}, count={count})"
837 );
838 return;
839 }
840 if let Some(p) = prev {
841 if i == p {
842 return;
843 }
844 if i < p {
845 debug_assert!(
846 i > p,
847 "range_extractor must emit sorted indexes (prev={p}, next={i})"
848 );
849 return;
850 }
851 debug_assert!(
852 i > p,
853 "range_extractor must emit sorted indexes (prev={p}, next={i})"
854 );
855 }
856 prev = Some(i);
857 f(i);
858 });
859 return;
860 }
861
862 let overscan = self.options.overscan;
863 let start = visible.start_index.saturating_sub(overscan);
864 let end = cmp::min(count, visible.end_index.saturating_add(overscan));
865 for i in start..end {
866 f(i);
867 }
868 }
869
870 pub fn for_each_virtual_item(&self, f: impl FnMut(VirtualItem)) {
871 self.for_each_virtual_item_for(self.scroll_offset, self.viewport_size, f);
872 }
873
874 pub fn for_each_virtual_item_for(
875 &self,
876 scroll_offset: u64,
877 viewport_size: u32,
878 mut f: impl FnMut(VirtualItem),
879 ) {
880 if !self.options.enabled {
881 return;
882 }
883
884 let visible = self.visible_range_for(scroll_offset, viewport_size);
885 if visible.is_empty() {
886 return;
887 }
888
889 if self.options.range_extractor.is_some() {
890 self.for_each_virtual_index_for(scroll_offset, viewport_size, |i| {
891 f(self.item(i));
892 });
893 return;
894 }
895
896 let count = self.options.count;
897 let overscan = self.options.overscan;
898 let start_index = visible.start_index.saturating_sub(overscan);
899 let end_index = cmp::min(count, visible.end_index.saturating_add(overscan));
900 if start_index >= end_index {
901 return;
902 }
903
904 let margin = self.options.scroll_margin as u64;
905 let mut start = margin.saturating_add(self.start_of(start_index));
906 let gap = self.options.gap as u64;
907
908 for i in start_index..end_index {
909 let size = self.sizes[i];
910 f(VirtualItem {
911 index: i,
912 start,
913 size,
914 });
915
916 start = start.saturating_add(size as u64);
917 if gap > 0 && i + 1 < count {
918 start = start.saturating_add(gap);
919 }
920 }
921 }
922
923 pub fn for_each_virtual_item_keyed(&self, f: impl FnMut(VirtualItemKeyed<K>)) {
924 self.for_each_virtual_item_keyed_for(self.scroll_offset, self.viewport_size, f);
925 }
926
927 pub fn for_each_virtual_item_keyed_for(
928 &self,
929 scroll_offset: u64,
930 viewport_size: u32,
931 mut f: impl FnMut(VirtualItemKeyed<K>),
932 ) {
933 if !self.options.enabled {
934 return;
935 }
936
937 let visible = self.visible_range_for(scroll_offset, viewport_size);
938 if visible.is_empty() {
939 return;
940 }
941
942 if self.options.range_extractor.is_some() {
943 self.for_each_virtual_index_for(scroll_offset, viewport_size, |i| {
944 let item = self.item(i);
945 f(VirtualItemKeyed {
946 key: self.key_for(i),
947 index: item.index,
948 start: item.start,
949 size: item.size,
950 });
951 });
952 return;
953 }
954
955 let count = self.options.count;
956 let overscan = self.options.overscan;
957 let start_index = visible.start_index.saturating_sub(overscan);
958 let end_index = cmp::min(count, visible.end_index.saturating_add(overscan));
959 if start_index >= end_index {
960 return;
961 }
962
963 let margin = self.options.scroll_margin as u64;
964 let mut start = margin.saturating_add(self.start_of(start_index));
965 let gap = self.options.gap as u64;
966
967 for i in start_index..end_index {
968 let size = self.sizes[i];
969 f(VirtualItemKeyed {
970 key: self.key_for(i),
971 index: i,
972 start,
973 size,
974 });
975
976 start = start.saturating_add(size as u64);
977 if gap > 0 && i + 1 < count {
978 start = start.saturating_add(gap);
979 }
980 }
981 }
982
983 pub fn scroll_to_index(&mut self, index: usize, align: Align) -> u64 {
993 let offset = self.scroll_to_index_offset(index, align);
994 self.set_scroll_offset(offset);
995 offset
996 }
997
998 pub fn scroll_to_index_offset(&self, index: usize, align: Align) -> u64 {
999 if !self.options.enabled {
1000 return self.options.initial_offset.resolve();
1001 }
1002 if self.options.count == 0 {
1003 return 0;
1004 }
1005 let index = index.min(self.options.count - 1);
1006 let item = self.item(index);
1007
1008 let sp_start = self.options.scroll_padding_start as u64;
1009 let sp_end = self.options.scroll_padding_end as u64;
1010 let view = self.viewport_size as u64;
1011
1012 let target = match align {
1013 Align::Start => item.start.saturating_sub(sp_start),
1014 Align::End => item.end().saturating_add(sp_end).saturating_sub(view),
1015 Align::Center => {
1016 let center = item.start.saturating_add(item.size as u64 / 2);
1017 center.saturating_sub(view / 2)
1018 }
1019 Align::Auto => {
1020 let cur = self.scroll_offset;
1021 let cur_end = cur.saturating_add(view);
1022 if item.start >= cur && item.end() <= cur_end {
1023 cur
1024 } else if item.start < cur {
1025 item.start.saturating_sub(sp_start)
1026 } else {
1027 item.end().saturating_add(sp_end).saturating_sub(view)
1028 }
1029 }
1030 };
1031
1032 self.clamp_scroll_offset(target)
1033 }
1034
1035 pub fn collect_virtual_indexes(&self, out: &mut Vec<usize>) {
1040 self.collect_virtual_indexes_for(self.scroll_offset, self.viewport_size, out);
1041 }
1042
1043 pub fn collect_virtual_indexes_for(
1047 &self,
1048 scroll_offset: u64,
1049 viewport_size: u32,
1050 out: &mut Vec<usize>,
1051 ) {
1052 out.clear();
1053 self.for_each_virtual_index_for(scroll_offset, viewport_size, |i| out.push(i));
1054 }
1055
1056 pub fn collect_virtual_items(&self, out: &mut Vec<VirtualItem>) {
1061 self.collect_virtual_items_for(self.scroll_offset, self.viewport_size, out);
1062 }
1063
1064 pub fn collect_virtual_items_for(
1068 &self,
1069 scroll_offset: u64,
1070 viewport_size: u32,
1071 out: &mut Vec<VirtualItem>,
1072 ) {
1073 out.clear();
1074 self.for_each_virtual_item_for(scroll_offset, viewport_size, |it| out.push(it));
1075 }
1076
1077 pub fn collect_virtual_items_keyed(&self, out: &mut Vec<VirtualItemKeyed<K>>) {
1081 self.collect_virtual_items_keyed_for(self.scroll_offset, self.viewport_size, out);
1082 }
1083
1084 pub fn collect_virtual_items_keyed_for(
1088 &self,
1089 scroll_offset: u64,
1090 viewport_size: u32,
1091 out: &mut Vec<VirtualItemKeyed<K>>,
1092 ) {
1093 out.clear();
1094 self.for_each_virtual_item_keyed_for(scroll_offset, viewport_size, |it| out.push(it));
1095 }
1096
1097 pub fn index_at_offset(&self, offset: u64) -> Option<usize> {
1098 if !self.options.enabled {
1099 return None;
1100 }
1101 self.index_at_offset_inner(offset)
1102 .filter(|&i| i < self.options.count)
1103 }
1104
1105 pub fn item_start(&self, index: usize) -> Option<u64> {
1106 if !self.options.enabled {
1107 return None;
1108 }
1109 (index < self.options.count).then(|| {
1110 let margin = self.options.scroll_margin as u64;
1111 margin.saturating_add(self.start_of(index))
1112 })
1113 }
1114
1115 pub fn item_size(&self, index: usize) -> Option<u32> {
1116 if !self.options.enabled {
1117 return None;
1118 }
1119 self.sizes.get(index).copied()
1120 }
1121
1122 pub fn item_end(&self, index: usize) -> Option<u64> {
1123 let start = self.item_start(index)?;
1124 let size = self.item_size(index)? as u64;
1125 Some(start.saturating_add(size))
1126 }
1127
1128 pub fn virtual_item_for_offset(&self, offset: u64) -> Option<VirtualItem> {
1129 let index = self.index_at_offset(offset)?;
1130 Some(self.item(index))
1131 }
1132
1133 pub fn virtual_item_keyed_for_offset(&self, offset: u64) -> Option<VirtualItemKeyed<K>> {
1134 let index = self.index_at_offset(offset)?;
1135 let item = self.item(index);
1136 Some(VirtualItemKeyed {
1137 key: self.key_for(index),
1138 index: item.index,
1139 start: item.start,
1140 size: item.size,
1141 })
1142 }
1143
1144 fn rebuild_estimates(&mut self) {
1145 vdebug!(
1146 count = self.options.count,
1147 cached = self.key_sizes.len(),
1148 "rebuild_estimates"
1149 );
1150 self.sizes.clear();
1151 self.measured.clear();
1152 self.sizes
1153 .reserve_exact(self.options.count.saturating_sub(self.sizes.len()));
1154 self.measured
1155 .reserve_exact(self.options.count.saturating_sub(self.measured.len()));
1156
1157 for i in 0..self.options.count {
1158 let key = self.key_for(i);
1159 if let Some(&measured_size) = self.key_sizes.get(&key) {
1160 self.sizes.push(measured_size);
1161 self.measured.push(true);
1162 } else {
1163 self.sizes.push((self.options.estimate_size)(i));
1164 self.measured.push(false);
1165 }
1166 }
1167 self.rebuild_fenwick();
1168 }
1169
1170 fn rebuild_fenwick(&mut self) {
1171 self.sums = Fenwick::from_sizes(&self.sizes, self.options.gap);
1172 }
1173
1174 fn resize_count(&mut self, prev_count: usize, new_count: usize) {
1175 if self.sizes.len() != prev_count
1176 || self.measured.len() != prev_count
1177 || self.sums.len() != prev_count
1178 {
1179 self.rebuild_estimates();
1182 return;
1183 }
1184
1185 let gap = self.options.gap as u64;
1186
1187 if new_count > prev_count {
1188 if gap > 0 && prev_count > 0 {
1189 self.sums.add(prev_count - 1, gap as i64);
1191 }
1192
1193 self.sizes.reserve_exact(new_count - prev_count);
1194 self.measured.reserve_exact(new_count - prev_count);
1195
1196 for i in prev_count..new_count {
1197 let key = self.key_for(i);
1198 let (size, is_measured) = if let Some(&measured_size) = self.key_sizes.get(&key) {
1199 (measured_size, true)
1200 } else {
1201 ((self.options.estimate_size)(i), false)
1202 };
1203
1204 self.sizes.push(size);
1205 self.measured.push(is_measured);
1206
1207 let mut value = size as u64;
1208 if gap > 0 && i + 1 < new_count {
1209 value = value.saturating_add(gap);
1210 }
1211 self.sums.push_value(value);
1212 }
1213 return;
1214 }
1215
1216 self.sizes.truncate(new_count);
1218 self.measured.truncate(new_count);
1219 self.sums.truncate(new_count);
1220
1221 if gap > 0 && new_count > 0 && new_count < prev_count {
1222 self.sums.add(new_count - 1, -(gap as i64));
1224 }
1225 }
1226
1227 fn item(&self, index: usize) -> VirtualItem {
1228 let margin = self.options.scroll_margin as u64;
1229 let start = margin.saturating_add(self.start_of(index));
1230 VirtualItem {
1231 index,
1232 start,
1233 size: self.sizes[index],
1234 }
1235 }
1236
1237 fn start_of(&self, index: usize) -> u64 {
1238 self.options.padding_start as u64 + self.sums.prefix_sum(index)
1239 }
1240
1241 pub fn max_scroll_offset(&self) -> u64 {
1242 if !self.options.enabled {
1243 return self.options.initial_offset.resolve();
1244 }
1245 let margin = self.options.scroll_margin as u64;
1246 let total = self.total_size();
1247 let view = self.viewport_size as u64;
1248 margin.saturating_add(total.saturating_sub(view))
1249 }
1250
1251 pub fn clamp_scroll_offset(&self, offset: u64) -> u64 {
1252 offset.min(self.max_scroll_offset())
1253 }
1254
1255 fn compute_range(&self, scroll_offset: u64, viewport_size: u32) -> VirtualRange {
1256 let mut range = self.compute_visible_range(scroll_offset, viewport_size);
1257 if range.is_empty() {
1258 return range;
1259 }
1260
1261 let count = self.options.count;
1262 let overscan = self.options.overscan;
1263 range.start_index = range.start_index.saturating_sub(overscan);
1264 range.end_index = cmp::min(count, range.end_index.saturating_add(overscan));
1265 range
1266 }
1267
1268 fn compute_visible_range(&self, scroll_offset: u64, viewport_size: u32) -> VirtualRange {
1269 let count = self.options.count;
1270 if count == 0 || viewport_size == 0 {
1271 return VirtualRange {
1272 start_index: 0,
1273 end_index: 0,
1274 };
1275 }
1276
1277 let margin = self.options.scroll_margin as u64;
1278 let view = viewport_size as u64;
1279
1280 let total = self.total_size();
1281 let max_scroll = margin.saturating_add(total.saturating_sub(view));
1282 let scroll_offset = scroll_offset.min(max_scroll);
1283 let scroll_end = scroll_offset.saturating_add(view);
1284 if scroll_end <= margin {
1285 return VirtualRange {
1286 start_index: 0,
1287 end_index: 0,
1288 };
1289 }
1290
1291 let off = scroll_offset.saturating_sub(margin);
1292 let visible_end_exclusive = scroll_end.saturating_sub(margin);
1293
1294 if off >= total {
1295 return VirtualRange {
1296 start_index: count,
1297 end_index: count,
1298 };
1299 }
1300
1301 let visible_start = off;
1302 let visible_end_inclusive = visible_end_exclusive.saturating_sub(1);
1303
1304 let mut start = self
1305 .index_at_offset_inner_list(visible_start)
1306 .unwrap_or(count);
1307 let mut end = self
1308 .index_at_offset_inner_list(cmp::max(visible_end_inclusive, visible_start))
1309 .map(|i| i + 1)
1310 .unwrap_or(count);
1311
1312 start = start.min(count);
1313 end = end.min(count);
1314
1315 VirtualRange {
1316 start_index: start,
1317 end_index: end,
1318 }
1319 }
1320
1321 fn index_at_offset_inner(&self, offset: u64) -> Option<usize> {
1322 let margin = self.options.scroll_margin as u64;
1323 if offset < margin {
1324 return Some(0);
1325 }
1326 self.index_at_offset_inner_list(offset - margin)
1327 }
1328
1329 fn index_at_offset_inner_list(&self, offset: u64) -> Option<usize> {
1330 let ps = self.options.padding_start as u64;
1331 if offset < ps {
1332 return Some(0);
1333 }
1334
1335 let off_in_items = offset - ps;
1336 let count = self.options.count;
1337 if count == 0 {
1338 return None;
1339 }
1340
1341 let consumed = self.sums.lower_bound(off_in_items);
1344 Some(consumed.min(count.saturating_sub(1)))
1345 }
1346}