Skip to main content

Module tree_view

Module tree_view 

Source
Expand description

TreeView — a virtualized, expandable/collapsible hierarchical list widget.

Displays a TreeModel<T> as an indented tree. Internally each view owns a TreeSlice for independent expand state, so two TreeViews on the same model can be open at different depths simultaneously. Only rows in the visible viewport + a small buffer have live widgets — rows outside the buffer are dormant, matching ListView’s virtualization model. An external TreeDataSource is also accepted via TreeView::from_source when the data lives outside a TreeModel.

Row heights come in three modes: uniform (item_height, default fast path), exact per-flat-index callback (item_height_fn), and auto-measured (auto_item_height — height-for-width per row, scroll-anchored).

§Scrolling to a row

handle returns a TreeViewHandle, a cheap Clone taken before the view is added and usable from any handler afterwards. Its scroll_to_index and ensure_index_visible take a flat visible row index and are applied at the next layout pass, after row measurement and scroll anchoring, so they may follow an expansion in the same turn, or be called before mount:

let tree = TreeView::new(model, |item, _entry, _selected| {
    Box::new(TextWidget::new(lit!(item)))
});
let handle = tree.handle();
// … `ctx.add(tree)`; then, from a handler, after expanding the row's
// ancestors in the same turn:
handle.ensure_index_visible(42);

§The keyboard cursor

The tree has one current row, its keyboard cursor: the row the arrow keys step from and the one it names to assistive technology as its active descendant while it holds keyboard focus. The same TreeViewHandle reads and moves it, in flat visible indices: cursor, set_cursor (clamped to the last visible row; keyboard focus, the selection and the scroll offset are left alone) and cursor_signal, for an application to observe. The cursor follows its row through an expansion or a collapse above it, whoever set it. A cursor set before mount is there at the first build.

In a single selection the cursor follows the selection. In a multiple selection the two are separate: Ctrl+↓ moves the cursor and leaves the selection, and a selection written from outside leaves the cursor where it was. Read the cursor, not the first selected row, for “the row the user is on”.

§Pan to scroll

The view installs common::scrollable::ScrollableBehavior, which gives it the shared wheel arithmetic, a finger’s pan and the PanClaim that puts it on a pan’s claimant chain. A pan scrolls it, the release coasts, and a pan it cannot absorb hands the whole event to the container outside. Vertical only: this view owns no horizontal offset, so a horizontal pan is declined and chains outward. A pan that starts on a row scrolls rather than activating it, toggling its chevron, or collapsing a multi-selection onto it — the three things a release on that row commits. Activation is a gesture, so the arbitration cancels it; the chevron and the deferred collapse are raw PointerUp arms the arbitration cannot reach, so each asks data_views::release_completes_the_press whether the release still belongs to the row.

§Keyboard

Arrows move the cursor; Home / End reach the first and last visible row and PageUp / PageDown a viewport of them, each moving the selection unless the accelerator is held, which moves the cursor alone — in a multiple selection only: a single selection always moves with the cursor, a selection the application sets moves the cursor onto it, and a structural change carries the selection along with its row. Shift extends a range from the anchor and Ctrl+Shift extends it additively.

→ opens a closed node and, on one already open, moves into its first child; ← closes an open node and, on a leaf or a closed one, ascends to the parent. Both mirror under RTL. * expands the whole subtree, + and - one level. Space selects or toggles, Enter activates, Ctrl+A and Ctrl+Shift+A select and deselect everything, and Ctrl+Arrow with Ctrl+Space build a disjoint selection. On macOS ⌘↓ opens a row, ⌘↑ ascends, and ⌥→/⌥← expand or collapse a subtree.

The full table, and why some of it is platform-specific, is in docs/data-view-keyboard.md.

§Example

let _w = TreeView::new(tree_model, |item, entry, _selected| {
    let indent = entry.depth as f32 * 20.0;
    Box::new(HStack::new()
        .child(Padding::new(0.0, 0.0, 0.0, indent))
        .child(TextWidget::new(lit!(&item.title))))
})
.item_height(28.0);

Structs§

TreeRowContext
Per-row context passed to a 4-arg TreeView delegate. Carries a reference to the slice handle and the row’s NodeId so the delegate can wire chevron toggles and other tree-aware behavior without manually cloning state outside the closure.
TreeView
TreeViewHandle
A handle on a mounted TreeView, to scroll it to a row from outside the widget — a view-model revealing the node a search found, say.