Skip to main content

iced_swdir_tree/directory_tree/
selection.rs

1//! Selection-mode type for click and keyboard interactions.
2//!
3//! [`SelectionMode`] controls how a new selection event composes
4//! with the existing [`DirectoryTree`](crate::DirectoryTree)
5//! selection state:
6//!
7//! | Mode                    | Effect on the selected set |
8//! |-------------------------|----------------------------|
9//! | [`SelectionMode::Replace`]      | Clear everything; the path becomes the single selection. |
10//! | [`SelectionMode::Toggle`]       | Add the path if absent; remove it if present. |
11//! | [`SelectionMode::ExtendRange`]  | Select every visible row between the anchor and the target, inclusive. The anchor is unchanged. |
12//!
13//! The built-in view emits clicks as [`SelectionMode::Replace`]
14//! because iced 0.14's `button::on_press` callback cannot observe
15//! modifier keys at press time. Applications that want multi-select
16//! track modifier state themselves (see `examples/multi_select.rs`)
17//! and rewrite the mode before forwarding the event to
18//! [`DirectoryTree::update`](crate::DirectoryTree::update).
19//!
20//! [`from_modifiers`](SelectionMode::from_modifiers) exists to make
21//! that rewrite trivial.
22
23use iced::keyboard::Modifiers;
24
25/// How an incoming selection event composes with the existing set.
26///
27/// The three modes cover the three standard click idioms:
28///
29/// | Mode                    | Effect on the selected set |
30/// |-------------------------|----------------------------|
31/// | [`SelectionMode::Replace`]     | Clear everything; the path becomes the single selection. |
32/// | [`SelectionMode::Toggle`]      | Add the path if absent; remove it if present. |
33/// | [`SelectionMode::ExtendRange`] | Select every visible row between the anchor and the target, inclusive. The anchor is unchanged. |
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum SelectionMode {
36    /// Clear any existing selection; the new path becomes the only
37    /// selected entry. This is what a plain left-click produces
38    /// from the built-in view.
39    #[default]
40    Replace,
41
42    /// Add the path to the selection if it isn't already there,
43    /// remove it otherwise. The anchor used for range extension is
44    /// updated to point at this path regardless of whether it was
45    /// added or removed.
46    Toggle,
47
48    /// Select every visible row between the current anchor and the
49    /// target path, inclusive, using the row order the widget
50    /// renders. The anchor itself is **not** moved — successive
51    /// range extensions all use the same starting pivot, which
52    /// matches how Windows Explorer, macOS Finder, and VS Code
53    /// behave.
54    ///
55    /// If the anchor is unset, or if either the anchor or the
56    /// target is not currently visible (filtered out, ancestor
57    /// collapsed, not yet loaded), the tree falls back to
58    /// [`SelectionMode::Replace`] semantics using just the target.
59    ExtendRange,
60}
61
62impl SelectionMode {
63    /// Pick the mode implied by the active modifier keys.
64    ///
65    /// The mapping is:
66    ///
67    /// * `Shift`   → [`SelectionMode::ExtendRange`]
68    /// * `Ctrl` or `Logo` (Command on macOS / Windows key elsewhere) → [`SelectionMode::Toggle`]
69    /// * otherwise → [`SelectionMode::Replace`]
70    ///
71    /// `Shift` wins over `Ctrl` / `Logo` when both are held. A
72    /// future release may distinguish "extend with toggle" (the
73    /// Windows Explorer `Ctrl+Shift+click` behavior) with a fourth
74    /// variant; for now `Ctrl+Shift+click` behaves as Shift-click.
75    ///
76    /// # Example
77    ///
78    /// ```ignore
79    /// use iced::keyboard::Modifiers;
80    /// use iced_swdir_tree::SelectionMode;
81    ///
82    /// // In your update handler, when you've received a click event
83    /// // and have tracked the current modifier state:
84    /// let mode = SelectionMode::from_modifiers(current_modifiers);
85    /// // Forward a rewritten Selected event to the widget.
86    /// ```
87    pub fn from_modifiers(modifiers: Modifiers) -> Self {
88        if modifiers.shift() {
89            Self::ExtendRange
90        } else if modifiers.control() || modifiers.logo() {
91            Self::Toggle
92        } else {
93            Self::Replace
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests;