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

ChartSelection

ChartSelection — point-level selection state for chart widgets.

ChartSelection manages which (series, point index) pairs are selected across a crate::ChartModel — the chart counterpart of crate::SelectionModel (flat lists) and crate::KeyedSelectionModel (keyed collections). It is a share-by-clone handle: pass a clone to each chart that should share selection state. The current selection is exposed as a reactive Signal<HashSet<(SeriesId, usize)>> so widgets can bind to it without polling.

HashSet (not BTreeSet) is used because SeriesId is intentionally not Ord (it's an opaque SlotMap key, mirroring crate::NodeId) — there is no natural ordering across series, only within one series' point indices. This is the same rationale as crate::KeyedSelectionModel, which uses HashSet<K> for the same reason.

Three selection behaviours are available via (toggle + anchor-based range extension). ChartSelection::extend_to only extends within the anchor's own series — a cross-series "range" has no natural order, so it falls back to a single-point select. ChartSelection::adjust keeps selected points consistent as the source model mutates (series removed, points inserted/removed) — call it from your own model observer, or skip the wiring entirely with ChartSelection::attached (equivalently, ChartSelection::attach on an existing selection), which subscribes internally and calls adjust for you, the same way crate::ChartWindow/crate::ChartAggregate self-wire in their own constructors. Forgetting to wire adjust up manually otherwise leaves the selection silently stale after a mutation.

#![allow(unused)]
fn main() {
use teksilo_data::{ChartModel, ChartSelection, SelectionMode};
let model: ChartModel<i32> = ChartModel::new();
let s = model.add_series("s");
for i in 0..5 {
    model.push_point(s, i, i as f32);
}

let sel = ChartSelection::attached(SelectionMode::Multi, &model);
sel.select_point(s, 1);
sel.extend_to(s, 3);
assert_eq!(sel.count(), 3); // (s,1), (s,2), (s,3)

model.remove_point(s, 0); // upstream mutation — no manual adjust() call
assert_eq!(sel.count(), 3); // (s,0), (s,1), (s,2) — shifted down

sel.clear();
assert_eq!(sel.count(), 0);
}

Builder methods at a glance

attached, attach, mode, selection_signal, is_selected, selected_points, count, select_point, toggle_point, extend_to, select_points, clear, adjust, prune, debug_named

API reference

📖 Full rustdoc API for this module

pub struct ChartSelection

Point-level selection state for a chart, keyed by (series, point index). See module documentation for semantics.

#![allow(unused)]
fn main() {
pub struct ChartSelection { /* fields */ }
}

Methods

pub fn new(mode: SelectionMode) -> Self

Create a new chart selection with the given mode.

pub fn attached<T: 'static>(mode: SelectionMode, model: &ChartModel<T>) -> Self

Create a selection that self-wires to model: every ChartChange the model emits is automatically routed through Self::adjust, so a point removed or shifted upstream never leaves a stale selected index behind. Equivalent to ChartSelection::new(mode) plus model.observe_changes(|c| sel.adjust(c)), minus the easy-to-forget wiring — mirrors how crate::ChartWindow and crate::ChartAggregate self-wire in their own constructors. The manual Self::adjust path still works — call it yourself instead if you'd rather relay through a custom change pipeline.

pub fn attach<T: 'static>(&self, model: &ChartModel<T>)

Subscribe this selection to model's changes, applying Self::adjust on every ChartChange. The subscription is held internally (shared across clones — see Clone), so it stays alive as long as any handle to this selection does; calling attach again (on this handle or any clone) drops the previous subscription and installs the new one.

The subscription closure captures only selection + anchor, not a full Self — capturing Self would pull in attach_handle too, which holds this very ObserverHandle, forming an Rc cycle that would leak the subscription instead of tearing down when every ChartSelection handle drops.

pub fn mode(&self) -> SelectionMode

The selection mode.

pub fn selection_signal(&self) -> Signal<HashSet<(SeriesId, usize)>>

A clone of the selection signal for reactive binding.

pub fn is_selected(&self, series: SeriesId, index: usize) -> bool

Whether (series, index) is currently selected.

pub fn selected_points(&self) -> Vec<(SeriesId, usize)>

The currently selected points (unordered snapshot).

pub fn count(&self) -> usize

Number of selected points.

pub fn select_point(&self, series: SeriesId, index: usize)

Select a single point, clearing the previous selection and setting the anchor.

pub fn toggle_point(&self, series: SeriesId, index: usize)

Toggle a point (Ctrl+click in Multi mode; acts as select_point in Single mode).

pub fn extend_to(&self, series: SeriesId, target: usize)

Extend the selection from the anchor to (series, target) (for Shift+click). Only extends within the anchor's own series — if the anchor is unset or belongs to a different series, falls back to a single-point select of (series, target).

pub fn select_points( &self, points: impl IntoIterator<Item = (SeriesId, usize)>, additive: bool, )

Replace the selection with points (or, when additive, union them into the current selection). Used by rubber-band / marquee selection. In Single mode an arbitrary one wins; None mode is a no-op.

pub fn clear(&self)

Clear the selection and anchor.

pub fn adjust(&self, change: &ChartChange)

React to an upstream ChartChange, keeping selection consistent with the model: a removed or wholesale-replaced series drops its selected points (and the anchor, if it pointed there); point insertions/removals shift or drop indices within their series. Series metadata changes (rename/recolor/visibility/move/insert) and in-place point updates never affect which points are selected.

pub fn prune(&self, exists: impl Fn(SeriesId, usize) -> bool)

Drop any selected point for which exists returns false.

pub fn debug_named(self, _name: impl Into<String>) -> Self

Register this selection with the debug inspector under name. In release builds (!cfg(debug_assertions)) this is a no-op pass-through so call sites stay free of #[cfg] lines.

Idempotent on repeated calls — the latest registration wins. The registration drops automatically when the last ChartSelection handle is freed (the strong adapter Rc lives inside a shared holder; the registry holds only a Weak).