pub struct Virtualizer<K = ItemKey> { /* private fields */ }Expand description
A headless virtualization engine.
This type is intentionally UI-agnostic:
- It does not hold any UI objects.
- Your adapter drives it by providing viewport geometry and scroll offsets.
- Rendering is exposed via zero-allocation iteration APIs (
for_each_virtual_*).
For smooth scrolling / tweens / anchoring patterns, see the virtualizer-adapter crate.
Implementations§
Source§impl<K: KeyCacheKey> Virtualizer<K>
impl<K: KeyCacheKey> Virtualizer<K>
Sourcepub fn new(options: VirtualizerOptions<K>) -> Self
pub fn new(options: VirtualizerOptions<K>) -> Self
Creates a new virtualizer from options.
If options.initial_rect and/or options.initial_offset are set, those values are applied
immediately.
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: measurements follow keys after reorder.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(2, |_| 1));
7 v.measure(0, 10);
8 println!(
9 "before reorder: size0={:?} size1={:?}",
10 v.item_size(0),
11 v.item_size(1)
12 );
13
14 // Simulate data reorder by changing the key mapping.
15 v.set_get_item_key(|i| if i == 0 { 1 } else { 0 });
16 // Note: in real apps you usually keep `get_item_key` stable and call `sync_item_keys()` when
17 // your underlying dataset is reordered while `count` stays the same.
18
19 println!(
20 "after reorder: size0={:?} size1={:?}",
21 v.item_size(0),
22 v.item_size(1)
23 );
24}4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}pub fn options(&self) -> &VirtualizerOptions<K>
pub fn set_options(&mut self, options: VirtualizerOptions<K>)
Sourcepub fn update_options(&mut self, f: impl FnOnce(&mut VirtualizerOptions<K>))
pub fn update_options(&mut self, f: impl FnOnce(&mut VirtualizerOptions<K>))
Clones the current options, applies f, then delegates to set_options.
This is useful when you want to update multiple options at once while letting the virtualizer decide what needs to be rebuilt (estimates/fenwick/reset).
pub fn set_on_change( &mut self, on_change: Option<impl Fn(&Virtualizer<K>, bool) + Send + Sync + 'static>, )
pub fn set_initial_offset(&mut self, initial_offset: u64)
pub fn set_initial_offset_provider( &mut self, initial_offset: impl Fn() -> u64 + Send + Sync + 'static, )
pub fn set_use_scrollend_event(&mut self, use_scrollend_event: bool)
pub fn set_is_scrolling_reset_delay_ms(&mut self, delay_ms: u64)
Sourcepub fn batch_update(&mut self, f: impl FnOnce(&mut Self))
pub fn batch_update(&mut self, f: impl FnOnce(&mut Self))
Batches multiple updates into a single on_change notification.
This is recommended for UI adapters: on a typical frame, you might update the scroll
rect, scroll offset, and is_scrolling state together. Without batching, each setter may
trigger on_change, which can be expensive if the callback drives rendering.
pub fn count(&self) -> usize
pub fn enabled(&self) -> bool
Sourcepub fn set_enabled(&mut self, enabled: bool)
pub fn set_enabled(&mut self, enabled: bool)
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}Sourcepub fn is_scrolling(&self) -> bool
pub fn is_scrolling(&self) -> bool
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn scroll_direction(&self) -> Option<ScrollDirection>
Sourcepub fn set_is_scrolling(&mut self, is_scrolling: bool)
pub fn set_is_scrolling(&mut self, is_scrolling: bool)
Examples found in repository?
47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}pub fn notify_scroll_event(&mut self, now_ms: u64)
Sourcepub fn update_scrolling(&mut self, now_ms: u64)
pub fn update_scrolling(&mut self, now_ms: u64)
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn viewport_size(&self) -> u32
Sourcepub fn scroll_rect(&self) -> Rect
pub fn scroll_rect(&self) -> Rect
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}Sourcepub fn viewport_state(&self) -> ViewportState
pub fn viewport_state(&self) -> ViewportState
Returns a lightweight snapshot of the current viewport state.
Sourcepub fn scroll_state(&self) -> ScrollState
pub fn scroll_state(&self) -> ScrollState
Returns a lightweight snapshot of the current scroll state.
Sourcepub fn frame_state(&self) -> FrameState
pub fn frame_state(&self) -> FrameState
Returns a combined snapshot of viewport + scroll state.
Sourcepub fn restore_viewport_state(&mut self, viewport: ViewportState)
pub fn restore_viewport_state(&mut self, viewport: ViewportState)
Restores viewport geometry from a previously captured snapshot.
Sourcepub fn restore_scroll_state(&mut self, scroll: ScrollState, now_ms: u64)
pub fn restore_scroll_state(&mut self, scroll: ScrollState, now_ms: u64)
Restores scroll state from a previously captured snapshot.
When scroll.is_scrolling is true, this will update the internal scrolling timers as if
a scroll event happened at now_ms.
Sourcepub fn restore_frame_state(&mut self, frame: FrameState, now_ms: u64)
pub fn restore_frame_state(&mut self, frame: FrameState, now_ms: u64)
Restores both viewport + scroll state from a previously captured snapshot.
When frame.scroll.is_scrolling is true, this will update the internal scrolling timers
as if a scroll event happened at now_ms.
pub fn set_scroll_rect(&mut self, rect: Rect)
Sourcepub fn apply_scroll_rect_event(&mut self, rect: Rect)
pub fn apply_scroll_rect_event(&mut self, rect: Rect)
Applies a scroll rect update from your UI layer.
Prefer this (or apply_scroll_frame*) over calling multiple setters when you have an
on_change callback that may trigger expensive work (like rendering/layout).
Sourcepub fn scroll_offset(&self) -> u64
pub fn scroll_offset(&self) -> u64
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn scroll_offset_in_list(&self) -> u64
pub fn set_viewport_size(&mut self, size: u32)
pub fn set_scroll_offset(&mut self, offset: u64)
Sourcepub fn apply_scroll_offset_event(&mut self, offset: u64, now_ms: u64)
pub fn apply_scroll_offset_event(&mut self, offset: u64, now_ms: u64)
Applies a scroll offset update from your UI layer (e.g. wheel/drag), and marks the virtualizer as scrolling.
Sourcepub fn set_scroll_offset_clamped(&mut self, offset: u64)
pub fn set_scroll_offset_clamped(&mut self, offset: u64)
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}Sourcepub fn apply_scroll_offset_event_clamped(&mut self, offset: u64, now_ms: u64)
pub fn apply_scroll_offset_event_clamped(&mut self, offset: u64, now_ms: u64)
Same as apply_scroll_offset_event, but clamps the offset.
Examples found in repository?
47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}Sourcepub fn set_viewport_and_scroll(
&mut self,
viewport_size: u32,
scroll_offset: u64,
)
pub fn set_viewport_and_scroll( &mut self, viewport_size: u32, scroll_offset: u64, )
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}Sourcepub fn set_viewport_and_scroll_clamped(
&mut self,
viewport_size: u32,
scroll_offset: u64,
)
pub fn set_viewport_and_scroll_clamped( &mut self, viewport_size: u32, scroll_offset: u64, )
Examples found in repository?
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}More examples
47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}Sourcepub fn apply_scroll_frame(
&mut self,
rect: Rect,
scroll_offset: u64,
now_ms: u64,
)
pub fn apply_scroll_frame( &mut self, rect: Rect, scroll_offset: u64, now_ms: u64, )
Applies both scroll rect and scroll offset in a single coalesced update.
This is the recommended entry point for UI adapters that receive scroll events along with updated viewport/rect information.
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}Sourcepub fn apply_scroll_frame_clamped(
&mut self,
rect: Rect,
scroll_offset: u64,
now_ms: u64,
)
pub fn apply_scroll_frame_clamped( &mut self, rect: Rect, scroll_offset: u64, now_ms: u64, )
Same as apply_scroll_frame, but clamps the offset.
pub fn set_count(&mut self, count: usize)
pub fn set_overscan(&mut self, overscan: usize)
pub fn set_padding(&mut self, padding_start: u32, padding_end: u32)
pub fn set_scroll_padding( &mut self, scroll_padding_start: u32, scroll_padding_end: u32, )
pub fn set_scroll_margin(&mut self, scroll_margin: u32)
pub fn set_gap(&mut self, gap: u32)
Sourcepub fn set_get_item_key(
&mut self,
f: impl Fn(usize) -> K + Send + Sync + 'static,
)
pub fn set_get_item_key( &mut self, f: impl Fn(usize) -> K + Send + Sync + 'static, )
Examples found in repository?
4fn main() {
5 // Example: measurements follow keys after reorder.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(2, |_| 1));
7 v.measure(0, 10);
8 println!(
9 "before reorder: size0={:?} size1={:?}",
10 v.item_size(0),
11 v.item_size(1)
12 );
13
14 // Simulate data reorder by changing the key mapping.
15 v.set_get_item_key(|i| if i == 0 { 1 } else { 0 });
16 // Note: in real apps you usually keep `get_item_key` stable and call `sync_item_keys()` when
17 // your underlying dataset is reordered while `count` stays the same.
18
19 println!(
20 "after reorder: size0={:?} size1={:?}",
21 v.item_size(0),
22 v.item_size(1)
23 );
24}More examples
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn set_should_adjust_scroll_position_on_item_size_change( &mut self, f: Option<impl Fn(&Virtualizer<K>, VirtualItem, i64) -> bool + Send + Sync + 'static>, )
pub fn sync_item_keys(&mut self)
pub fn set_range_extractor( &mut self, f: Option<impl Fn(Range, &mut dyn FnMut(usize)) + Send + Sync + 'static>, )
pub fn set_estimate_size( &mut self, f: impl Fn(usize) -> u32 + Send + Sync + 'static, )
pub fn reset_measurements(&mut self)
Sourcepub fn measurement_cache_len(&self) -> usize
pub fn measurement_cache_len(&self) -> usize
Returns the number of cached measured sizes (key → size).
Examples found in repository?
4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}Sourcepub fn for_each_cached_size(&self, f: impl FnMut(&K, u32))
pub fn for_each_cached_size(&self, f: impl FnMut(&K, u32))
Iterates over the cached measured sizes (key → size) without allocations.
Sourcepub fn export_measurement_cache(&self) -> Vec<(K, u32)>where
K: Clone,
pub fn export_measurement_cache(&self) -> Vec<(K, u32)>where
K: Clone,
Exports the cached measured sizes as a Vec (useful for persistence).
Examples found in repository?
4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}Sourcepub fn import_measurement_cache(
&mut self,
entries: impl IntoIterator<Item = (K, u32)>,
)
pub fn import_measurement_cache( &mut self, entries: impl IntoIterator<Item = (K, u32)>, )
Replaces the cached measured sizes from an iterator (useful when restoring state).
Note: this rebuilds internal per-index sizes using the current key mapping.
Examples found in repository?
4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}Sourcepub fn measure(&mut self, index: usize, size: u32)
pub fn measure(&mut self, index: usize, size: u32)
Marks an item as measured and updates its cached size.
This is aligned with TanStack Virtual’s “measure element” behavior: if the measured item is
before the current scroll offset, the virtualizer may adjust scroll_offset to prevent a
visible “jump”.
If you want to update measurements without any scroll adjustment, use
measure_unadjusted.
Examples found in repository?
4fn main() {
5 // Example: measurements follow keys after reorder.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(2, |_| 1));
7 v.measure(0, 10);
8 println!(
9 "before reorder: size0={:?} size1={:?}",
10 v.item_size(0),
11 v.item_size(1)
12 );
13
14 // Simulate data reorder by changing the key mapping.
15 v.set_get_item_key(|i| if i == 0 { 1 } else { 0 });
16 // Note: in real apps you usually keep `get_item_key` stable and call `sync_item_keys()` when
17 // your underlying dataset is reordered while `count` stays the same.
18
19 println!(
20 "after reorder: size0={:?} size1={:?}",
21 v.item_size(0),
22 v.item_size(1)
23 );
24}More examples
4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}Sourcepub fn measure_keyed(&mut self, index: usize, key: K, size: u32)
pub fn measure_keyed(&mut self, index: usize, key: K, size: u32)
Same as Self::measure, but uses a precomputed key to avoid recomputing get_item_key.
Sourcepub fn measure_unadjusted(&mut self, index: usize, size: u32)
pub fn measure_unadjusted(&mut self, index: usize, size: u32)
Marks an item as measured and updates its cached size without adjusting scroll_offset.
Examples found in repository?
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}Sourcepub fn measure_keyed_unadjusted(&mut self, index: usize, key: K, size: u32)
pub fn measure_keyed_unadjusted(&mut self, index: usize, key: K, size: u32)
Same as Self::measure_unadjusted, but uses a precomputed key.
Sourcepub fn resize_item(&mut self, index: usize, size: u32) -> i64
pub fn resize_item(&mut self, index: usize, size: u32) -> i64
Examples found in repository?
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}More examples
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn resize_item_keyed(&mut self, index: usize, key: K, size: u32) -> i64
Sourcepub fn measure_many(
&mut self,
measurements: impl IntoIterator<Item = (usize, u32)>,
)
pub fn measure_many( &mut self, measurements: impl IntoIterator<Item = (usize, u32)>, )
Measures multiple items in one pass.
Like Self::measure, this may adjust scroll_offset to prevent jumps.
Sourcepub fn measure_many_unadjusted(
&mut self,
measurements: impl IntoIterator<Item = (usize, u32)>,
)
pub fn measure_many_unadjusted( &mut self, measurements: impl IntoIterator<Item = (usize, u32)>, )
Measures multiple items without adjusting scroll_offset.
pub fn resize_item_many( &mut self, measurements: impl IntoIterator<Item = (usize, u32)>, ) -> i64
pub fn is_measured(&self, index: usize) -> bool
Sourcepub fn total_size(&self) -> u64
pub fn total_size(&self) -> u64
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn key_for(&self, index: usize) -> K
Sourcepub fn virtual_range(&self) -> VirtualRange
pub fn virtual_range(&self) -> VirtualRange
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}pub fn virtual_range_for( &self, scroll_offset: u64, viewport_size: u32, ) -> VirtualRange
Sourcepub fn visible_range(&self) -> VirtualRange
pub fn visible_range(&self) -> VirtualRange
Examples found in repository?
47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}More examples
6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn visible_range_for( &self, scroll_offset: u64, viewport_size: u32, ) -> VirtualRange
Sourcepub fn for_each_virtual_index(&self, f: impl FnMut(usize))
pub fn for_each_virtual_index(&self, f: impl FnMut(usize))
Examples found in repository?
6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}pub fn for_each_virtual_index_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(usize), )
Sourcepub fn for_each_virtual_item(&self, f: impl FnMut(VirtualItem))
pub fn for_each_virtual_item(&self, f: impl FnMut(VirtualItem))
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}6fn main() {
7 // Example: pinned/sticky "headers" at fixed indexes.
8 let mut opts = VirtualizerOptions::new(1_000, |_| 1);
9 opts.overscan = 2;
10
11 let pinned: Arc<[usize]> = Arc::from([0usize, 10, 20, 30, 40, 999]);
12 opts.range_extractor = Some(Arc::new({
13 let pinned = Arc::clone(&pinned);
14 move |r: Range, emit: &mut dyn FnMut(usize)| {
15 let mut e = IndexEmitter::new(r, emit);
16 // IMPORTANT: indexes must be emitted in ascending order.
17 //
18 // We want pinned rows both before and after the overscanned range. To keep the output
19 // sorted, emit:
20 // 1) pinned indexes before the overscanned range
21 // 2) the overscanned contiguous range
22 // 3) pinned indexes after the overscanned range
23 let overscanned_start = r.start_index.saturating_sub(r.overscan);
24 let overscanned_end = r.end_index.saturating_add(r.overscan).min(r.count);
25
26 for &idx in pinned.iter() {
27 if idx < overscanned_start {
28 e.emit_pinned(idx);
29 }
30 }
31
32 e.emit_overscanned();
33
34 for &idx in pinned.iter() {
35 if idx >= overscanned_end {
36 e.emit_pinned(idx);
37 }
38 }
39 }
40 }));
41
42 let mut v = Virtualizer::new(opts);
43 v.set_viewport_and_scroll_clamped(10, 500);
44
45 let mut collected = Vec::new();
46 v.for_each_virtual_index(|i| collected.push(i));
47
48 println!("visible_range={:?}", v.visible_range());
49 println!("virtual_range={:?}", v.virtual_range());
50 println!(
51 "indexes_len={} first_20={:?}",
52 collected.len(),
53 &collected[..20.min(collected.len())]
54 );
55
56 // A real UI would typically iterate items:
57 let mut headers = 0usize;
58 v.for_each_virtual_item(|it| {
59 if pinned.binary_search(&it.index).is_ok() {
60 headers += 1;
61 }
62 });
63 println!("pinned_headers_in_output={headers}");
64}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn for_each_virtual_item_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(VirtualItem), )
Sourcepub fn for_each_virtual_item_keyed(&self, f: impl FnMut(VirtualItemKeyed<K>))
pub fn for_each_virtual_item_keyed(&self, f: impl FnMut(VirtualItemKeyed<K>))
Examples found in repository?
7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}pub fn for_each_virtual_item_keyed_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(VirtualItemKeyed<K>), )
Sourcepub fn scroll_to_index(&mut self, index: usize, align: Align) -> u64
pub fn scroll_to_index(&mut self, index: usize, align: Align) -> u64
Programmatically scrolls to an index (no animation).
This sets the internal scroll_offset to the computed (clamped) target and triggers
on_change. It does not mark the virtualizer as “scrolling”.
If you want “user scrolling” semantics (e.g. to drive is_scrolling debouncing), call
apply_scroll_offset_event_clamped(scroll_to_index_offset(...), now_ms) instead.
Returns the applied (clamped) offset.
Sourcepub fn scroll_to_index_offset(&self, index: usize, align: Align) -> u64
pub fn scroll_to_index_offset(&self, index: usize, align: Align) -> u64
Examples found in repository?
4fn main() {
5 let mut v = Virtualizer::new(VirtualizerOptions::new(1_000_000, |_| 1));
6 v.set_viewport_and_scroll(10, 123_456);
7
8 let mut items = Vec::new();
9 v.for_each_virtual_item(|it| items.push(it));
10 println!("total_size={}", v.total_size());
11 println!("visible_range={:?}", v.virtual_range());
12 println!("first_visible={:?}", items.first());
13
14 let off = v.scroll_to_index_offset(999_999, Align::End);
15 v.set_scroll_offset_clamped(off);
16 println!("after scroll_to_index: offset={}", v.scroll_offset());
17}More examples
4fn main() {
5 // Example: dynamic measurement + scroll jump prevention.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(100, |_| 10));
7 v.set_viewport_and_scroll_clamped(30, 200);
8
9 println!(
10 "before: off={} total={} range={:?}",
11 v.scroll_offset(),
12 v.total_size(),
13 v.virtual_range()
14 );
15
16 // If an item before the viewport changes size, `resize_item` can adjust scroll_offset to
17 // prevent visual jumps.
18 let applied = v.resize_item(0, 30);
19 println!(
20 "resize_item(0): applied_delta={applied} off={} total={}",
21 v.scroll_offset(),
22 v.total_size()
23 );
24
25 // Measuring an item updates sizes and may adjust scroll (TanStack-aligned behavior).
26 v.measure(1, 50);
27 println!(
28 "measure(1): off={} total={}",
29 v.scroll_offset(),
30 v.total_size()
31 );
32
33 // If you want to update measurements without changing scroll, use `measure_unadjusted`.
34 v.measure_unadjusted(2, 30);
35
36 // Scroll-to helpers still work with updated measurements.
37 let to = v.scroll_to_index_offset(10, Align::Start);
38 v.set_scroll_offset_clamped(to);
39 println!(
40 "set_scroll_offset_clamped(scroll_to_index_offset(10)): off={} range={:?}",
41 v.scroll_offset(),
42 v.virtual_range()
43 );
44}47fn main() {
48 // Simulate an adapter that drives scroll_offset with a tween.
49 let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50 v.set_viewport_and_scroll_clamped(20, 0);
51
52 let to = v.scroll_to_index_offset(2_000, Align::Center);
53 let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55 let mut now_ms = 0u64;
56 let mut frame = 0u64;
57
58 loop {
59 // Simulate a 60fps "tick".
60 now_ms = now_ms.saturating_add(16);
61 frame += 1;
62
63 // Adapter samples tween and writes it into the virtualizer.
64 let off = tween.sample(now_ms);
65 v.apply_scroll_offset_event_clamped(off, now_ms);
66
67 // Render: a UI would iterate items and draw them.
68 let mut first: Option<usize> = None;
69 let mut last: Option<usize> = None;
70 v.for_each_virtual_item(|it| {
71 first.get_or_insert(it.index);
72 last = Some(it.index);
73 });
74
75 if frame.is_multiple_of(5) {
76 println!(
77 "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78 v.scroll_offset(),
79 v.visible_range(),
80 first,
81 last
82 );
83 }
84
85 // Simulate user input interrupting the animation:
86 // at ~120ms, retarget to a new index.
87 if (120..120 + 16).contains(&now_ms) {
88 let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89 tween.retarget(now_ms, new_to, 300);
90 }
91
92 if tween.is_done(now_ms) {
93 break;
94 }
95 }
96
97 // Mark scroll as finished for adapters that use this state.
98 v.set_is_scrolling(false);
99
100 println!(
101 "done: off={} range={:?}",
102 v.scroll_offset(),
103 v.virtual_range()
104 );
105}7fn main() {
8 // Simulate a framework adapter that owns the scroll state.
9 let saved_scroll = Arc::new(AtomicU64::new(120));
10
11 let opts = VirtualizerOptions::new(1000, |_| 1)
12 .with_initial_rect(Some(Rect {
13 main: 10,
14 cross: 80,
15 }))
16 .with_initial_offset_provider({
17 let saved_scroll = Arc::clone(&saved_scroll);
18 move || saved_scroll.load(Ordering::Relaxed)
19 })
20 .with_scroll_margin(5)
21 .with_range_extractor(Some(|r: Range, emit: &mut dyn FnMut(usize)| {
22 // Pin a "sticky header" at index 0, regardless of scroll position.
23 let mut e = virtualizer::IndexEmitter::new(r, emit);
24 e.emit_pinned(0);
25 e.emit_visible();
26 }));
27
28 let mut v = Virtualizer::new(opts);
29
30 // First render: scroll offset comes from the provider.
31 println!("initial scroll_offset={}", v.scroll_offset());
32 println!("initial scroll_rect={:?}", v.scroll_rect());
33
34 // Adapter updates rect + scroll offset on events.
35 v.apply_scroll_frame(
36 Rect {
37 main: 12,
38 cross: 80,
39 },
40 200,
41 0,
42 );
43
44 let mut items = Vec::new();
45 v.for_each_virtual_item_keyed(|it| items.push(it));
46 println!(
47 "is_scrolling={}, visible_range={:?}, items_len={}",
48 v.is_scrolling(),
49 v.visible_range(),
50 items.len()
51 );
52 println!("first_item={:?}", items.first());
53
54 // Demonstrate scroll-to helpers.
55 let target = v.scroll_to_index_offset(500, Align::Start);
56 v.set_scroll_offset_clamped(target);
57 println!("after scroll_to_index: scroll_offset={}", v.scroll_offset());
58
59 // Demonstrate dynamic measurement + scroll adjustment.
60 let applied = v.resize_item(0, 20);
61 println!("resize_item applied_scroll_adjustment={applied}");
62
63 // Simulate reorder: change key mapping. This automatically rebuilds per-index sizes from the
64 // key-based measurement cache. In real apps, you usually keep `get_item_key` stable and call
65 // `sync_item_keys()` when your dataset is reordered while `count` stays the same.
66 v.set_get_item_key(|i| if i == 0 { 1 } else { i as u64 });
67
68 // Debounced scrolling reset without relying on a native scrollend event.
69 v.update_scrolling(200);
70 println!("after update_scrolling: is_scrolling={}", v.is_scrolling());
71
72 // Toggle enabled to disable all queries.
73 v.set_enabled(false);
74 let mut disabled_len = 0usize;
75 v.for_each_virtual_item(|_| disabled_len += 1);
76 println!(
77 "disabled total_size={}, items_len={}",
78 v.total_size(),
79 disabled_len
80 );
81}Sourcepub fn collect_virtual_indexes(&self, out: &mut Vec<usize>)
pub fn collect_virtual_indexes(&self, out: &mut Vec<usize>)
Collects virtual item indexes into out (clears out first).
This is a convenience wrapper around Self::for_each_virtual_index. For maximum
performance, prefer for_each_virtual_index and reuse a scratch buffer in your adapter.
Sourcepub fn collect_virtual_indexes_for(
&self,
scroll_offset: u64,
viewport_size: u32,
out: &mut Vec<usize>,
)
pub fn collect_virtual_indexes_for( &self, scroll_offset: u64, viewport_size: u32, out: &mut Vec<usize>, )
Collects virtual item indexes into out for a given scroll_offset/viewport_size.
This clears out first.
Sourcepub fn collect_virtual_items(&self, out: &mut Vec<VirtualItem>)
pub fn collect_virtual_items(&self, out: &mut Vec<VirtualItem>)
Collects virtual items into out (clears out first).
This is a convenience wrapper around Self::for_each_virtual_item. For maximum
performance, prefer for_each_virtual_item and reuse a scratch buffer in your adapter.
Sourcepub fn collect_virtual_items_for(
&self,
scroll_offset: u64,
viewport_size: u32,
out: &mut Vec<VirtualItem>,
)
pub fn collect_virtual_items_for( &self, scroll_offset: u64, viewport_size: u32, out: &mut Vec<VirtualItem>, )
Collects virtual items into out for a given scroll_offset/viewport_size.
This clears out first.
Sourcepub fn collect_virtual_items_keyed(&self, out: &mut Vec<VirtualItemKeyed<K>>)
pub fn collect_virtual_items_keyed(&self, out: &mut Vec<VirtualItemKeyed<K>>)
Collects keyed virtual items into out (clears out first).
This is a convenience wrapper around Self::for_each_virtual_item_keyed.
Sourcepub fn collect_virtual_items_keyed_for(
&self,
scroll_offset: u64,
viewport_size: u32,
out: &mut Vec<VirtualItemKeyed<K>>,
)
pub fn collect_virtual_items_keyed_for( &self, scroll_offset: u64, viewport_size: u32, out: &mut Vec<VirtualItemKeyed<K>>, )
Collects keyed virtual items into out for a given scroll_offset/viewport_size.
This clears out first.
pub fn index_at_offset(&self, offset: u64) -> Option<usize>
pub fn item_start(&self, index: usize) -> Option<u64>
Sourcepub fn item_size(&self, index: usize) -> Option<u32>
pub fn item_size(&self, index: usize) -> Option<u32>
Examples found in repository?
4fn main() {
5 // Example: measurements follow keys after reorder.
6 let mut v = Virtualizer::new(VirtualizerOptions::new(2, |_| 1));
7 v.measure(0, 10);
8 println!(
9 "before reorder: size0={:?} size1={:?}",
10 v.item_size(0),
11 v.item_size(1)
12 );
13
14 // Simulate data reorder by changing the key mapping.
15 v.set_get_item_key(|i| if i == 0 { 1 } else { 0 });
16 // Note: in real apps you usually keep `get_item_key` stable and call `sync_item_keys()` when
17 // your underlying dataset is reordered while `count` stays the same.
18
19 println!(
20 "after reorder: size0={:?} size1={:?}",
21 v.item_size(0),
22 v.item_size(1)
23 );
24}More examples
4fn main() {
5 // Example: export and import measurement cache (key -> measured size).
6 //
7 // This is useful if you want to persist measurements across screens/sessions so that the
8 // virtualizer can start with better estimates and avoid re-measuring everything.
9 let mut v1 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
10 v1.measure(2, 10);
11 v1.measure(5, 42);
12
13 let snapshot = v1.export_measurement_cache();
14 println!("exported_cache_len={}", snapshot.len());
15
16 let mut v2 = Virtualizer::new(VirtualizerOptions::new(10, |_| 1));
17 println!(
18 "before import: size2={:?} size5={:?}",
19 v2.item_size(2),
20 v2.item_size(5)
21 );
22
23 v2.import_measurement_cache(snapshot);
24 println!(
25 "after import: cache_len={} size2={:?} size5={:?}",
26 v2.measurement_cache_len(),
27 v2.item_size(2),
28 v2.item_size(5)
29 );
30}pub fn item_end(&self, index: usize) -> Option<u64>
pub fn virtual_item_for_offset(&self, offset: u64) -> Option<VirtualItem>
pub fn virtual_item_keyed_for_offset( &self, offset: u64, ) -> Option<VirtualItemKeyed<K>>
pub fn max_scroll_offset(&self) -> u64
pub fn clamp_scroll_offset(&self, offset: u64) -> u64
Trait Implementations§
Source§impl<K: Clone> Clone for Virtualizer<K>
impl<K: Clone> Clone for Virtualizer<K>
Source§fn clone(&self) -> Virtualizer<K>
fn clone(&self) -> Virtualizer<K>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more