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}