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

KeyedTreeCheckedModel

KeyedTreeCheckedModel<K> — per-node checkbox state for a tree keyed by a stable domain id, with optional descendant→ancestor tristate aggregation.

The keyed counterpart of TreeCheckedModel — the checkbox twin of KeyedSelectionModel. Where TreeCheckedModel is bound to a TreeModel<T> and keyed by NodeId, this model is keyed by your domain key K (an entity id, a tagged enum) and takes the tree shape as two injected closures (children + parent), so it composes over a TreeDataSlice or any TreeDataSource — the "select scenes to export" tristate over an external outline, without mirroring into a TreeModel.

Because identity is the domain key (stable across a full re-source), a node's check state survives the tree reloading — a checked scene stays checked after the backend refreshes. Use prune_missing after a reload to drop the state of nodes that no longer exist.

Semantics, cascade behaviour, the Signal<CheckState> / Signal<bool> bridge, and the re-entry guard are identical to TreeCheckedModel — see its module docs for the detail. This model is a share-by-clone handle (Rc<RefCell<…>> internally).

Example

use teksilo_data::{KeyedTreeCheckedModel, CheckState, TreeDataSlice, TreeRow};

// An outline: Binder(1) → { Chapter(2) → Scene(3), Scene(4) }
let slice: TreeDataSlice<u64, &str> = TreeDataSlice::from_rows(vec![
    TreeRow::new(1, "Binder", 0),
    TreeRow::new(2, "Chapter", 1),
    TreeRow::new(3, "Scene A", 2),
    TreeRow::new(4, "Scene B", 1),
]);

let checked = KeyedTreeCheckedModel::from_source(slice.clone());
let _ = (checked.signal_for(1), checked.signal_for(2), checked.signal_for(3), checked.signal_for(4));

checked.check(3);                                         // one scene under the chapter
assert_eq!(checked.check_state(&2), CheckState::Checked); // chapter has only Scene A → Checked
assert_eq!(checked.check_state(&1), CheckState::Indeterminate); // Binder: 2 of {chapter, Scene B}

Builder methods at a glance

from_source, with_mode, aggregate_mode, set_aggregate_mode, signal_for, bool_signal_for, check_state, check, uncheck, toggle, checked_keys, clear, prune_missing, reaggregate

API reference

📖 Full rustdoc API for this module

pub struct KeyedTreeCheckedModel

Per-node checkbox state for a domain-keyed tree, with optional descendant→ancestor tristate aggregation. See the module docs.

#![allow(unused)]
fn main() {
pub struct KeyedTreeCheckedModel<K: ItemKey> { /* fields */ }
}

Methods

pub fn new( children: impl Fn(&K) -> Vec<K> + 'static, parent: impl Fn(&K) -> Option<K> + 'static, ) -> Self

Create a model over a tree whose shape is given by two closures: children(key) -> Vec<K> and parent(key) -> Option<K>. Uses the default AggregateMode::DescendantsDriveAncestors.

pub fn from_source<S>(source: S) -> Self where S: TreeDataSource<Key = K> + Clone + 'static,

Create a model whose tree shape is read from a cloneable TreeDataSource (e.g. a TreeDataSlice). The source is cloned into the shape closures, so the model reflects the live tree — call prune_missing after the source reloads to drop state for removed nodes.

pub fn with_mode(self, mode: AggregateMode) -> Self

Set the AggregateMode at construction.

pub fn aggregate_mode(&self) -> AggregateMode

The current AggregateMode.

pub fn set_aggregate_mode(&self, mode: AggregateMode)

Change the cascade behaviour; takes effect on the next write.

pub fn signal_for(&self, key: K) -> Signal<CheckState>

Writable Signal<CheckState> for key (cached). External writes trigger the configured aggregation pass. The cascade observer is wired idempotently — including for a signal first materialised by a cascade (write_state) before its own signal_for was ever called — so binding a lazily-realised (e.g. virtualized) row still cascades on write.

pub fn bool_signal_for(&self, key: K) -> Signal<bool>

Two-state Signal<bool> projection of signal_for (cached, writable). Checked → true; anything else → false. See crate::TreeCheckedModel::bool_signal_for.

pub fn check_state(&self, key: &K) -> CheckState

The current CheckState for key (Unchecked if never touched).

pub fn check(&self, key: K)

Set key to CheckState::Checked (triggers cascade + ancestor recompute).

pub fn uncheck(&self, key: K)

Set key to CheckState::Unchecked (triggers cascade + ancestor recompute).

pub fn toggle(&self, key: K)

Toggle key: a leaf under DescendantsDriveAncestors cycles two-state; a branch or AggregateMode::None cycles the full tristate sequence.

pub fn checked_keys(&self) -> Vec<K>

All keys whose current state is exactly CheckState::Checked. May include stale keys after a tree mutation — call prune_missing or filter against the current tree yourself.

pub fn clear(&self)

Reset all known nodes to CheckState::Unchecked.

Writes every tracked key directly via the internal write_state helper (per-key cascade-suppressed) instead of signal_for(..).set(..)'s normal path, which would, for every currently-checked key, cascade the write down its whole descendant subtree and recompute every ancestor up to the root — redundant here, since every tracked key ends up Unchecked and "all children unchecked" is already the correct parent aggregate. See TreeCheckedModel::clear for the non-keyed twin of this same optimization.

pub fn prune_missing(&self, exists: impl Fn(&K) -> bool)

Drop cached check state (and its signals/observers) for every key for which exists(&key) returns false, then reaggregate surviving parents against the current tree. Call after a reload so a deleted node's state doesn't linger in checked_keys() and the ancestors it used to affect show the correct tristate. Mirrors crate::KeyedSelectionModel::prune_missing.

pub fn reaggregate(&self)

Recompute every surviving parent's aggregate from the current tree shape + leaf states, deepest first. Call after the backing tree's structure changed (a reload that added/removed/moved nodes) so parent tristates reflect the new children; prune_missing does this for you. A no-op under AggregateMode::None.