Virtualizer

Struct Virtualizer 

Source
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>

Source

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?
examples/basic.rs (line 5)
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
Hide additional examples
examples/keyed_reorder.rs (line 6)
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}
examples/measurement_cache.rs (line 9)
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}
examples/dynamic_measurement.rs (line 6)
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}
examples/tween_scroll.rs (line 49)
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}
examples/pinned_headers.rs (line 42)
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}
Source

pub fn options(&self) -> &VirtualizerOptions<K>

Source

pub fn set_options(&mut self, options: VirtualizerOptions<K>)

Source

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).

Source

pub fn set_on_change( &mut self, on_change: Option<impl Fn(&Virtualizer<K>, bool) + Send + Sync + 'static>, )

Source

pub fn set_initial_offset(&mut self, initial_offset: u64)

Source

pub fn set_initial_offset_provider( &mut self, initial_offset: impl Fn() -> u64 + Send + Sync + 'static, )

Source

pub fn set_use_scrollend_event(&mut self, use_scrollend_event: bool)

Source

pub fn set_is_scrolling_reset_delay_ms(&mut self, delay_ms: u64)

Source

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.

Source

pub fn count(&self) -> usize

Source

pub fn enabled(&self) -> bool

Source

pub fn set_enabled(&mut self, enabled: bool)

Examples found in repository?
examples/adapter_sim.rs (line 73)
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}
Source

pub fn is_scrolling(&self) -> bool

Examples found in repository?
examples/adapter_sim.rs (line 48)
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}
Source

pub fn scroll_direction(&self) -> Option<ScrollDirection>

Source

pub fn set_is_scrolling(&mut self, is_scrolling: bool)

Examples found in repository?
examples/tween_scroll.rs (line 98)
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}
Source

pub fn notify_scroll_event(&mut self, now_ms: u64)

Source

pub fn update_scrolling(&mut self, now_ms: u64)

Examples found in repository?
examples/adapter_sim.rs (line 69)
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}
Source

pub fn viewport_size(&self) -> u32

Source

pub fn scroll_rect(&self) -> Rect

Examples found in repository?
examples/adapter_sim.rs (line 32)
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}
Source

pub fn viewport_state(&self) -> ViewportState

Returns a lightweight snapshot of the current viewport state.

Source

pub fn scroll_state(&self) -> ScrollState

Returns a lightweight snapshot of the current scroll state.

Source

pub fn frame_state(&self) -> FrameState

Returns a combined snapshot of viewport + scroll state.

Source

pub fn restore_viewport_state(&mut self, viewport: ViewportState)

Restores viewport geometry from a previously captured snapshot.

Source

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.

Source

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.

Source

pub fn set_scroll_rect(&mut self, rect: Rect)

Source

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).

Source

pub fn scroll_offset(&self) -> u64

Examples found in repository?
examples/basic.rs (line 16)
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
Hide additional examples
examples/dynamic_measurement.rs (line 11)
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}
examples/tween_scroll.rs (line 53)
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}
examples/adapter_sim.rs (line 31)
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}
Source

pub fn scroll_offset_in_list(&self) -> u64

Source

pub fn set_viewport_size(&mut self, size: u32)

Source

pub fn set_scroll_offset(&mut self, offset: u64)

Source

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.

Source

pub fn set_scroll_offset_clamped(&mut self, offset: u64)

Examples found in repository?
examples/basic.rs (line 15)
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
Hide additional examples
examples/dynamic_measurement.rs (line 38)
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}
examples/adapter_sim.rs (line 56)
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}
Source

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?
examples/tween_scroll.rs (line 65)
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}
Source

pub fn set_viewport_and_scroll( &mut self, viewport_size: u32, scroll_offset: u64, )

Examples found in repository?
examples/basic.rs (line 6)
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}
Source

pub fn set_viewport_and_scroll_clamped( &mut self, viewport_size: u32, scroll_offset: u64, )

Examples found in repository?
examples/dynamic_measurement.rs (line 7)
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
Hide additional examples
examples/tween_scroll.rs (line 50)
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}
examples/pinned_headers.rs (line 43)
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}
Source

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?
examples/adapter_sim.rs (lines 35-42)
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}
Source

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.

Source

pub fn set_count(&mut self, count: usize)

Source

pub fn set_overscan(&mut self, overscan: usize)

Source

pub fn set_padding(&mut self, padding_start: u32, padding_end: u32)

Source

pub fn set_scroll_padding( &mut self, scroll_padding_start: u32, scroll_padding_end: u32, )

Source

pub fn set_scroll_margin(&mut self, scroll_margin: u32)

Source

pub fn set_gap(&mut self, gap: u32)

Source

pub fn set_get_item_key( &mut self, f: impl Fn(usize) -> K + Send + Sync + 'static, )

Examples found in repository?
examples/keyed_reorder.rs (line 15)
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
Hide additional examples
examples/adapter_sim.rs (line 66)
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}
Source

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>, )

Source

pub fn sync_item_keys(&mut self)

Source

pub fn set_range_extractor( &mut self, f: Option<impl Fn(Range, &mut dyn FnMut(usize)) + Send + Sync + 'static>, )

Source

pub fn set_estimate_size( &mut self, f: impl Fn(usize) -> u32 + Send + Sync + 'static, )

Source

pub fn reset_measurements(&mut self)

Source

pub fn measurement_cache_len(&self) -> usize

Returns the number of cached measured sizes (key → size).

Examples found in repository?
examples/measurement_cache.rs (line 26)
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}
Source

pub fn for_each_cached_size(&self, f: impl FnMut(&K, u32))

Iterates over the cached measured sizes (key → size) without allocations.

Source

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?
examples/measurement_cache.rs (line 13)
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}
Source

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?
examples/measurement_cache.rs (line 23)
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}
Source

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?
examples/keyed_reorder.rs (line 7)
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
Hide additional examples
examples/measurement_cache.rs (line 10)
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}
examples/dynamic_measurement.rs (line 26)
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}
Source

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.

Source

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?
examples/dynamic_measurement.rs (line 34)
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}
Source

pub fn measure_keyed_unadjusted(&mut self, index: usize, key: K, size: u32)

Same as Self::measure_unadjusted, but uses a precomputed key.

Source

pub fn resize_item(&mut self, index: usize, size: u32) -> i64

Examples found in repository?
examples/dynamic_measurement.rs (line 18)
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
Hide additional examples
examples/adapter_sim.rs (line 60)
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}
Source

pub fn resize_item_keyed(&mut self, index: usize, key: K, size: u32) -> i64

Source

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.

Source

pub fn measure_many_unadjusted( &mut self, measurements: impl IntoIterator<Item = (usize, u32)>, )

Measures multiple items without adjusting scroll_offset.

Source

pub fn resize_item_many( &mut self, measurements: impl IntoIterator<Item = (usize, u32)>, ) -> i64

Source

pub fn is_measured(&self, index: usize) -> bool

Source

pub fn total_size(&self) -> u64

Examples found in repository?
examples/basic.rs (line 10)
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
Hide additional examples
examples/dynamic_measurement.rs (line 12)
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}
examples/adapter_sim.rs (line 78)
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}
Source

pub fn key_for(&self, index: usize) -> K

Source

pub fn virtual_range(&self) -> VirtualRange

Examples found in repository?
examples/basic.rs (line 11)
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
Hide additional examples
examples/dynamic_measurement.rs (line 13)
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}
examples/tween_scroll.rs (line 103)
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}
examples/pinned_headers.rs (line 49)
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}
Source

pub fn virtual_range_for( &self, scroll_offset: u64, viewport_size: u32, ) -> VirtualRange

Source

pub fn visible_range(&self) -> VirtualRange

Examples found in repository?
examples/tween_scroll.rs (line 79)
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
Hide additional examples
examples/pinned_headers.rs (line 48)
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}
examples/adapter_sim.rs (line 49)
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}
Source

pub fn visible_range_for( &self, scroll_offset: u64, viewport_size: u32, ) -> VirtualRange

Source

pub fn for_each_virtual_index(&self, f: impl FnMut(usize))

Examples found in repository?
examples/pinned_headers.rs (line 46)
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}
Source

pub fn for_each_virtual_index_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(usize), )

Source

pub fn for_each_virtual_item(&self, f: impl FnMut(VirtualItem))

Examples found in repository?
examples/basic.rs (line 9)
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
Hide additional examples
examples/tween_scroll.rs (lines 70-73)
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}
examples/pinned_headers.rs (lines 58-62)
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}
examples/adapter_sim.rs (line 75)
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}
Source

pub fn for_each_virtual_item_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(VirtualItem), )

Source

pub fn for_each_virtual_item_keyed(&self, f: impl FnMut(VirtualItemKeyed<K>))

Examples found in repository?
examples/adapter_sim.rs (line 45)
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}
Source

pub fn for_each_virtual_item_keyed_for( &self, scroll_offset: u64, viewport_size: u32, f: impl FnMut(VirtualItemKeyed<K>), )

Source

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.

Source

pub fn scroll_to_index_offset(&self, index: usize, align: Align) -> u64

Examples found in repository?
examples/basic.rs (line 14)
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
Hide additional examples
examples/dynamic_measurement.rs (line 37)
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}
examples/tween_scroll.rs (line 52)
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}
examples/adapter_sim.rs (line 55)
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}
Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn index_at_offset(&self, offset: u64) -> Option<usize>

Source

pub fn item_start(&self, index: usize) -> Option<u64>

Source

pub fn item_size(&self, index: usize) -> Option<u32>

Examples found in repository?
examples/keyed_reorder.rs (line 10)
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
Hide additional examples
examples/measurement_cache.rs (line 19)
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}
Source

pub fn item_end(&self, index: usize) -> Option<u64>

Source

pub fn virtual_item_for_offset(&self, offset: u64) -> Option<VirtualItem>

Source

pub fn virtual_item_keyed_for_offset( &self, offset: u64, ) -> Option<VirtualItemKeyed<K>>

Source

pub fn max_scroll_offset(&self) -> u64

Source

pub fn clamp_scroll_offset(&self, offset: u64) -> u64

Trait Implementations§

Source§

impl<K: Clone> Clone for Virtualizer<K>

Source§

fn clone(&self) -> Virtualizer<K>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<K: Debug> Debug for Virtualizer<K>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<K = u64> !Freeze for Virtualizer<K>

§

impl<K = u64> !RefUnwindSafe for Virtualizer<K>

§

impl<K> Send for Virtualizer<K>
where K: Send,

§

impl<K = u64> !Sync for Virtualizer<K>

§

impl<K> Unpin for Virtualizer<K>
where K: Unpin,

§

impl<K = u64> !UnwindSafe for Virtualizer<K>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more