-
Notifications
You must be signed in to change notification settings - Fork 9
Widget VirtualList
MeowLynxSea edited this page Jan 24, 2026
·
5 revisions
A virtualized list widget based on gpui::list(ListState, ...).
VirtualList is the widget-layer companion to the VirtualRow component.
-
VirtualListowns the scrolling + virtualization behavior. -
VirtualRowowns the per-row identity + spacing/divider contract.
If you’re building a list of “rows”, the recommended pattern is:
VirtualList (widget) + VirtualRow (row shell) + ListItem (row content).
- Long scrolling lists (settings pages, search results, file trees)
- Any UI where you want smooth scrolling and you can structure content as rows
Not a good fit:
- A single giant scroll view that is one huge item (virtualization can’t help if one item is huge)
Widget constructors:
-
virtual_list(state, render_row) -> VirtualList: build the widget from list state -
virtual_list_state(item_count, alignment, overdraw) -> gpui::ListState: build list state
State helpers:
-
VirtualListController::new(state)reset(element_count)splice(old_range, count)scroll_to_reveal_item(ix)
-
VirtualListHandle::new(item_count, alignment, overdraw)state()controller()
- Hold
gpui::ListState(orVirtualListHandle) at the view level. - Keep
render_rowcheap: virtualization helps, but visible rows should still be fast.
If row content can change height after render (expand/collapse, async-loaded content), notify the list so it can re-measure.
Common choices:
- simplest:
controller.reset(new_count) - fine-grained updates:
controller.splice(ix..ix + 1, 1)
-
VirtualListusesListSizingBehavior::default(). - Overdraw is whatever you pass into
virtual_list_state(...)/VirtualListHandle::new(...).
use gpui::{AnyElement, ListAlignment, px};
use yororen_ui::{
component::{list_item, virtual_row},
widget::{virtual_list, virtual_list_state, VirtualListController},
};
struct MyView {
state: gpui::ListState,
controller: VirtualListController,
items: Vec<String>,
}
impl MyView {
fn new(cx: &mut gpui::App) -> Self {
let state = virtual_list_state(0, ListAlignment::Top, px(256.));
let controller = VirtualListController::new(state.clone());
Self { state, controller, items: Vec::new() }
}
fn render(&mut self) -> impl gpui::IntoElement {
let items = self.items.clone();
virtual_list(self.state.clone(), move |ix, _window, _cx| -> AnyElement {
virtual_row(("row", ix))
.child(list_item().content(items.get(ix).cloned().unwrap_or_default()))
.into_any_element()
})
}
}
## Example: Using `VirtualListHandle`
`VirtualListHandle` is a convenience type that bundles the state + controller.
```rust
use gpui::{AnyElement, ListAlignment, px};
use yororen_ui::{
component::{list_item, virtual_row},
widget::{virtual_list, VirtualListHandle},
};
struct MyView {
list: VirtualListHandle,
items: Vec<String>,
}
impl MyView {
fn new() -> Self {
Self {
list: VirtualListHandle::new(0, ListAlignment::Top, px(256.)),
items: Vec::new(),
}
}
fn set_items(&mut self, items: Vec<String>) {
let old_len = self.items.len();
let new_len = items.len();
self.items = items;
self.list.controller().splice(0..old_len, new_len);
}
fn render(&mut self) -> impl gpui::IntoElement {
let items = self.items.clone();
virtual_list(self.list.state(), move |ix, _window, _cx| -> AnyElement {
virtual_row(("row", ix))
.child(list_item().content(items.get(ix).cloned().unwrap_or_default()))
.into_any_element()
})
}
}
## Notes
- Always render rows using `VirtualRow(key)` to ensure stable identity under virtualization.
- When item count or row height changes, notify via `VirtualListController.reset/splice`.
Yororen UI v0.3.0 · repository · Apache-2.0 · This wiki documents Yororen UI v0.3.0.
This wiki documents Yororen UI v0.3.0 — the headless-core, swappable-renderer build.