Skip to main content

Module tree_checked_model

Module tree_checked_model 

Source
Expand description

TreeCheckedModel — per-node checkbox state for a tree, with optional descendant→ancestor tristate aggregation.

Companion to crate::CheckedModel for trees. Defaults to the standard “Outlook folder selection” semantic: a parent’s state is Checked if all descendants are checked, Unchecked if none, Indeterminate otherwise; toggling a parent cascades Checked/Unchecked down to all descendants. Set the mode to AggregateMode::None to give every node independent state instead. The model is a share-by-clone handle (Rc<RefCell<…>> internally) — cloning produces a second view onto the same checkbox state.

External writes (e.g. a Checkbox widget bound to signal_for(node) setting it directly) trigger the same cascade-and-recompute pass as the model’s own check/uncheck/toggle methods, via per-node observers. A re-entry guard prevents the cascade pass from re-firing observers it triggers itself.

§Example

let tree = TreeModel::new();
let root = tree.insert_root(0, "root");
let child_a = tree.insert_child(root, 0, "a");
let child_b = tree.insert_child(root, 1, "b");

let model = TreeCheckedModel::new(tree);
// Pre-register signal chains before mutating.
let _ = (model.signal_for(root), model.signal_for(child_a), model.signal_for(child_b));

model.check(child_a);
assert_eq!(model.check_state(root), CheckState::Indeterminate);
model.check(child_b);
assert_eq!(model.check_state(root), CheckState::Checked);

§Limitation: tree-mutation desync

signal_for(node) and bool_signal_for(node) cache signals keyed by NodeId. The cache is never invalidated. If the underlying TreeModel<T> mutates (remove, move_node, etc.) the cached entry for a removed NodeId lingers indefinitely:

  • checked_nodes() may include a stale NodeId whose underlying tree node no longer exists. Callers that consume this list should validate each id against the current tree state before acting on it.
  • bool_signal_for / signal_for for a removed node still return their cached signal handle. Setting it has no observable effect on the tree (the cascade walks tree.children(node) which is empty for a freed node).

This is an acceptable trade-off because NodeIds are not reused by TreeModel (slotmap keys are versioned), so a stale id can never alias a fresh node. If a future use case needs strict invalidation on removal, subscribe to TreeModel’s change events and clear the relevant entries. Tracked as out-of-scope for V1.

Structs§

TreeCheckedModel
Per-node checkbox state for a TreeModel<T>, with optional descendant→ancestor tristate aggregation.

Enums§

AggregateMode
How a parent’s CheckState relates to its descendants.