Skip to main content

guise/data/
virtuallist.rs

1//! `VirtualList` — windowed rendering for large flat collections.
2//!
3//! Wraps gpui's `uniform_list`: only the rows in view are built each frame,
4//! so a 100k-item list renders as cheaply as a 20-item one. Items come from
5//! a factory closure (not pre-built children) and must share one height —
6//! that uniformity is what makes the scroll math O(1).
7
8use std::rc::Rc;
9
10use crate::devtools::Probed;
11use gpui::prelude::*;
12use gpui::{px, uniform_list, AnyElement, App, ElementId, IntoElement, Window};
13
14type ItemBuilder = Rc<dyn Fn(usize, &mut Window, &mut App) -> AnyElement + 'static>;
15
16/// A virtualized list. `VirtualList::new("log", 100_000, |i, _, _| row(i)).height(400.0)`.
17#[derive(IntoElement)]
18pub struct VirtualList {
19    id: ElementId,
20    count: usize,
21    height: f32,
22    item: ItemBuilder,
23}
24
25impl VirtualList {
26    /// `item` is invoked per visible index, every frame — keep it cheap and
27    /// return rows of equal height.
28    pub fn new<E>(
29        id: impl Into<ElementId>,
30        count: usize,
31        item: impl Fn(usize, &mut Window, &mut App) -> E + 'static,
32    ) -> Self
33    where
34        E: IntoElement,
35    {
36        VirtualList {
37            id: id.into(),
38            count,
39            height: 240.0,
40            item: Rc::new(move |ix, window, cx| item(ix, window, cx).into_any_element()),
41        }
42    }
43
44    /// Viewport height in px (default `240.0`).
45    pub fn height(mut self, height: f32) -> Self {
46        self.height = height.max(0.0);
47        self
48    }
49}
50
51impl RenderOnce for VirtualList {
52    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
53        let item = self.item;
54        uniform_list(self.id, self.count, move |range, window, cx| {
55            range.map(|ix| item(ix, window, cx)).collect::<Vec<_>>()
56        })
57        .h(px(self.height))
58        .w_full()
59        .probe("VirtualList")
60    }
61}