Skip to main content

dioxus_swdir_tree_core/
selection.rs

1//! Selection state and mode types for [`crate::DirectoryTree`].
2//!
3//! The authoritative selection is held **by path** on the tree root —
4//! never by node reference — so it survives node rebuilds, filter
5//! changes, and re-merges transparently.
6//!
7//! Per-node [`crate::TreeNode::is_selected`] flags are derived view
8//! hints re-synced after every mutation by the `sync_flags` helper.
9
10use std::collections::HashSet;
11use std::path::PathBuf;
12
13use crate::node::TreeNode;
14
15/// How a click or keyboard gesture modifies the selection set.
16///
17/// Modifier-key mapping (Ctrl → `Toggle`, Shift → `ExtendRange`) is
18/// handled by the component layer (RFC 006) because keyboard modifiers
19/// arrive on the event object, not in widget state.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum SelectionMode {
22    /// Clear the selection and select only this path.
23    ///
24    /// Clicking an already-selected row still results in exactly that
25    /// one row selected (S3.4 — no implicit deselect-on-re-click).
26    Replace,
27
28    /// Add if absent; remove if already selected. Updates the anchor.
29    Toggle,
30
31    /// Select the contiguous range from the current anchor to this path
32    /// in [`crate::DirectoryTree::visible_rows`] order.
33    ///
34    /// If no anchor is set, behaves as [`SelectionMode::Replace`].
35    /// **The anchor does not move** (S6.3): repeated Shift-clicks grow
36    /// or shrink the range from the same pivot.
37    ExtendRange,
38}
39
40/// Re-derive the per-node `is_selected` flag for every loaded node.
41///
42/// Builds a `HashSet` over the path references so the walk is
43/// O(N_loaded) in node visits — not O(N_loaded × M_selected).
44pub(crate) fn sync_flags(root: &mut TreeNode, selected_paths: &[PathBuf]) {
45    let set: HashSet<&PathBuf> = selected_paths.iter().collect();
46    sync_node(root, &set);
47}
48
49fn sync_node(node: &mut TreeNode, selected: &HashSet<&PathBuf>) {
50    node.is_selected = selected.contains(&node.path);
51    for child in &mut node.children {
52        sync_node(child, selected);
53    }
54}