Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Data models

Use teksilo-data to share observable collections between application state and views. Cloning a model handle shares its state; it does not copy the collection.

Minimal example

use teksilo_data::ListModel;

fn main() {
    let items = ListModel::from_vec(vec![String::from("First")]);
    let _observer = items.observe_changes(|change| println!("{change:?}"));
    items.push(String::from("Second"));
    assert_eq!(items.len(), 2);
}

Keep the observer handle alive while the subscription is needed. Drop it to unsubscribe. A view observes its model to update its displayed items.

Choose a model

NeedType
In-memory listListModel<T>
Sorted or filtered listSortFilterListModel<T>
In-memory hierarchyTreeModel<T>
Independent expansion of a shared treeTreeSlice<T>
Sorted or filtered treeSortFilterTreeModel<T>
External or lazily loaded dataListDataSource or TreeDataSource
External outline with stable domain keysTreeDataSlice<K, T> and TreeRowFilter<K, T>
Shared row selectionSelectionModel or keyed selection models
Check stateCheckedModel, TreeCheckedModel, or KeyedTreeCheckedModel
Chart seriesChartModel<T>

Common operations

Use push, insert, remove, set, or clear to change a list. Read an item through with_item(index, callback); the callback scopes the internal borrow. Use reconcile_by_key when replacing external data while preserving identity. Do not mutate a model while holding a borrow into it.

Use Repeater for small, changing groups of widgets. Use ListView, TableView, or GridView when rows or tiles need virtualization. For trees, keep expansion state per view when two views must expand independently.

To bring an item into view after changing the model, do not compute a scroll offset from the model: take the view's handle (ListView::handle, TreeView::handle, TableView::handle, TreeTableView::handle) and call its scroll_to_index / ensure_index_visible (scroll_to_row / ensure_row_visible for the tables). The view applies the request at its next layout pass, once the change has reached the row heights, so it may follow the mutation in the same turn. See table-view.md.

The same handle carries the view's keyboard cursor for ListView, TreeView and GridView: cursor(), set_cursor(Some(i)) and a read-only cursor_signal() (the tables expose focused_cell_signal() / set_focused_cell on the view). In a multiple selection the cursor and the selection are separate (Ctrl+Arrow moves the cursor alone), so code acting on "the row the user is on" reads the cursor. Setting it moves the active descendant, and the grid's focus ring, without moving keyboard focus or the selection; it does not scroll, so follow it with ensure_index_visible.

A view can share its selection model with another view. Match the selection key type to the source: index-based identity and stable domain identity have different behavior when items move or are filtered.

For a database, filesystem, or paged service, implement a data-source trait rather than copying the entire dataset into a second in-memory model. See data sources for loading, identity, and drag-and-drop contracts.

Chart data

ChartModel stores series and points. ChartWindow and ChartAggregate provide windowing and aggregation building blocks; applications compose them explicitly. ChartSelection can be shared by chart widgets. See charts.

Constraints

  • Model handles use UI-thread ownership. Pass background results to the UI before mutating them; do not share the handles across threads.
  • Keep subscriptions and projection handles alive while a view depends on them.
  • Stable keys are required when selection must survive reordering or replacement.
  • A reactive model is not persistent storage. Use settings or the application's database for persistence.

Reference

Engineering reference

Implementation details and review history are retained in the repository.