Expand description
TreeTableView<T> — hierarchical multi-column data table with expand/collapse.
Sibling of TableView for tree-shaped data. Each row carries
a depth level; one designated column (the tree column, defaulting to the first)
shows a twist (chevron) and an indent gutter that toggles the row’s children.
Backed by a SortFilterTreeModel<T> so sort, filter, and expand state compose
without extra bookkeeping. Shares the header (a hosted
TableHeader), column, keyboard, and selection
modules with TableView.
Rows live in a TreeBodyPane — a sibling of the scrollbar — so buffer-exit /
selection / expand rebuilds are never deferred mid-thumb-drag. Three row-height
modes: uniform (row_height, fast path), exact per-flat-index callback
(row_height_fn), and auto-measured (auto_row_height — grows to tallest cell).
§Common patterns
A checkbox column. Selection and “checked” are different things — a
checkbox column wants its own state, with parent/child propagation. Build it
from TreeCheckedModel over the same tree
the view projects.
A cell delegate receives (&T, &CellContext) and CellContext carries no
node identity — only row_index. So
capture the projection and resolve the row’s NodeId through it:
let proxy = SortFilterTreeModel::new(tree);
let checks = TreeCheckedModel::new(proxy.tree());
let for_cells = proxy.clone();
let col = Column::new("done", lit!("Done"), move |_item, cx: &CellContext| {
match for_cells.visible_node_id(cx.row_index) {
Some(node) => Box::new(Checkbox::new(checks.check_state(node))) as Box<dyn Widget>,
None => Box::new(Spacer::new()),
}
});For a tree whose identity is a domain key rather than a NodeId, use
KeyedTreeCheckedModel instead — it
survives a full re-source, which a NodeId-keyed set cannot.
Group rows that span the table, and the group kept in sight. A tree
whose upper levels are groups — songs under albums under artists — draws
those rows as one band across every column with
full_width_row and
full_width_row_delegate, and
keeps the groups being scrolled through pinned under the header with
pinned_ancestors:
let view = TreeTableView::from_projection(proxy)
.columns(columns)
.full_width_row(|row: &Row| !row.is_song())
.full_width_row_delegate(|row, _cx: &CellContext| {
Box::new(TextWidget::new(lit!(row.summary()))) as Box<dyn Widget>
})
.pinned_ancestors(2); // the artist, then the albumA band is still a row of the tree — it selects, drags, expands from its chevron and answers type-ahead — and one cell to the keyboard and to assistive technology. The pinned rows are copies, pushed up as the next group arrives, hidden from assistive technology, and every reveal stops below them. The two builders say the rest.
§Accessibility
Root emits Role::TreeGrid; rows carry set_level + set_expanded.
ArrowLeft / ArrowRight on the tree column collapse / expand. A full-width
row holds one cell, spanning every column.
// Column delegates capture closures — use ignore.
use teksilo_widgets::TreeTableView;
use teksilo_data::TreeModel;
let _view = TreeTableView::new(model).row_height(28.0);§Scrolling to a row
handle returns a TreeTableViewHandle, a
cheap Clone taken before the view is added and usable from any handler
afterwards. Its scroll_to_row and
ensure_row_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 view = TreeTableView::new(model);
let handle = view.handle();
// … add the columns, `ctx.add(view)`; then, from a handler, after
// expanding the row's ancestors in the same turn:
handle.ensure_row_visible(42);The builder-value methods of the same names compute the offset when they are called, against the last layout pass.
§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 — never a residual. A pan that starts on a row scrolls
rather than activating it or collapsing a multi-selection onto it. Both axes
are claimed. Shift+wheel still
scrolls the columns, and a finger’s pan is never remapped by a held Shift:
the remap is a wheel convention, and turning a drag sideways is not what
the hand asked for.
Structs§
- Tree
Table View - Hierarchical multi-column widget. See module documentation.
- Tree
Table View Handle - A handle on a mounted
TreeTableView, to scroll it to a row from outside the widget — a view-model revealing the node a search found, say.