-
Notifications
You must be signed in to change notification settings - Fork 9
Guide Recipes ListTreeRow
MeowLynxSea edited this page Jun 18, 2026
·
3 revisions
A tree-row pattern with a collapsible disclosure inside a virtual_list. The key detail: when a row's height changes, notify the list via controller.splice(...) so the virtualised list re-measures that row.
use yororen_ui::headless::badge::badge;
use yororen_ui::headless::disclosure::disclosure;
use yororen_ui::headless::label::label;
use yororen_ui::headless::list_item::list_item;
use yororen_ui::headless::virtual_list::{VirtualListController, virtual_list};
#[derive(Default)]
pub struct TreeExpansion { pub expanded: BTreeSet<usize> }
impl gpui::Global for TreeExpansion {}
pub struct TreeView {
pub rows: Vec<String>,
pub controller: VirtualListController,
}
impl Render for TreeView {
fn render(&mut self, _w: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let controller = self.controller.clone();
let rows = self.rows.clone();
let entity = cx.entity();
virtual_list("recipe:tree", &controller, cx)
.row(move |ix, _w, cx| {
let row_id = format!("recipe:tree-row-{ix}");
let label_text = rows.get(ix).cloned().unwrap_or_default();
let is_expanded = cx.try_global::<TreeExpansion>()
.map(|g| g.expanded.contains(&ix)).unwrap_or(false);
let entity_for_click = entity.clone();
let controller_for_click = controller.clone();
list_item(row_id.clone(), label_text.clone(), cx)
.on_click(move |_ev, _w, cx| {
cx.update_global::<TreeExpansion, _>(|state, _cx| {
if !state.expanded.insert(ix) { state.expanded.remove(&ix); }
});
controller_for_click.splice(ix..ix + 1, 1);
})
.render(cx)
.child(disclosure(format!("{row_id}-disc"), if is_expanded { "Hide" } else { "Show" }, &mut *cx)
.open(is_expanded).render(&mut *cx))
.child(label(format!("{row_id}-label"), label_text, cx).strong(true).render(cx))
.child(badge(format!("{row_id}-badge"),
SharedString::from(if is_expanded { "Expanded" } else { "Collapsed" }), cx
).render(cx))
.into_any_element()
})
.render(cx)
}
}-
Stable keys from your data model. Use the data's primary key (e.g.
"row-{}", node.id) when the tree can reorder. -
Height changes need
splice. When the row's height changes (disclosure opens), callcontroller.splice(ix..ix+1, 1)sogpui::listre-measures that row. Without it, stale overdraw leaves visible gaps. -
list_item.on_clickisFn(&ClickEvent, &mut Window, &mut App)— three args. -
Disclosure chevron is in the renderer. You don't have to draw it; pass
.open(true|false)and the renderer rotates the icon. -
virtual_listis closure-driven. Avoidcx.spawnor file I/O inside the.row(...)closure. -
Append-only streaming: prefer
controller.append(n)overcontroller.reset(n)—appendpreserves the user's scroll position.
- Disclosure — full API.
- ListItem — full API.
- VirtualList — full API.
- Recipe: context menu.
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.