Skip to main content

iced_swdir_tree/directory_tree/
message.rs

1//! Message types.
2//!
3//! [`DirectoryTreeEvent`] is the single message type flowing in and out
4//! of the widget. Two variants are user-facing (`Toggled`, `Selected`);
5//! a third (`Loaded`) carries async scan results and is opaque — parent
6//! applications pass it through `update` without needing to introspect
7//! it.
8
9use std::path::PathBuf;
10use std::sync::Arc;
11
12use super::drag::DragMsg;
13use super::node::LoadedEntry;
14use super::selection::SelectionMode;
15
16/// A message emitted by or consumed by the [`DirectoryTree`](crate::DirectoryTree) widget.
17///
18/// ## For parent applications
19///
20/// Wrap this in one of your own `Message` variants:
21///
22/// ```ignore
23/// enum MyMessage {
24///     Tree(iced_swdir_tree::DirectoryTreeEvent),
25///     // ...
26/// }
27/// ```
28///
29/// Route every `Tree(event)` to [`DirectoryTree::update`] and map its
30/// returned `Task` back. Pattern-match on `Toggled` / `Selected` *before*
31/// forwarding if you want app-level side effects (e.g. previewing the
32/// selected file):
33///
34/// ```ignore
35/// fn update(&mut self, msg: MyMessage) -> Task<MyMessage> {
36///     match msg {
37///         MyMessage::Tree(event) => {
38///             if let DirectoryTreeEvent::Selected(path, _, _) = &event {
39///                 self.preview(path);
40///             }
41///             self.tree.update(event).map(MyMessage::Tree)
42///         }
43///     }
44/// }
45/// ```
46///
47/// [`DirectoryTree::update`]: crate::DirectoryTree::update
48#[derive(Debug, Clone)]
49pub enum DirectoryTreeEvent {
50    /// A folder was toggled open/closed by the user.
51    ///
52    /// On first expansion the widget issues an async scan whose result
53    /// arrives later as [`DirectoryTreeEvent::Loaded`]. Subsequent
54    /// toggles of the same folder are instant — children stay in the
55    /// internal cache.
56    Toggled(PathBuf),
57
58    /// A row was selected.
59    ///
60    /// The `bool` indicates whether the path is a directory (`true`)
61    /// or a file (`false`). The [`SelectionMode`] controls how the
62    /// click composes with any existing selection — see its docs for
63    /// the full matrix.
64    ///
65    /// The built-in view always emits this with
66    /// [`SelectionMode::Replace`] because iced 0.14's button
67    /// callbacks cannot observe modifier keys at press time.
68    /// Applications that want multi-select track modifier state
69    /// themselves (see `examples/multi_select.rs`) and rewrite the
70    /// mode before forwarding the event — [`SelectionMode::from_modifiers`]
71    /// makes that a one-liner.
72    Selected(PathBuf, bool, SelectionMode),
73
74    /// Internal drag-machinery event.
75    ///
76    /// Emitted by the widget's built-in view as the user presses a
77    /// row, moves across others, and releases. Applications should
78    /// treat these as opaque and always route them back to
79    /// [`DirectoryTree::update`](crate::DirectoryTree::update) —
80    /// just like [`Loaded`](Self::Loaded).
81    ///
82    /// The widget's internal state machine may produce a
83    /// [`Selected`](Self::Selected) or a [`DragCompleted`](Self::DragCompleted)
84    /// as the downstream effect of a [`Drag`](Self::Drag) message.
85    /// Apps observe those downstream events via the usual
86    /// `.map(MyMessage::Tree)` routing — no extra plumbing needed.
87    Drag(DragMsg),
88
89    /// The user completed a drag gesture with intent to move (or
90    /// otherwise transplant) `sources` into `destination`.
91    ///
92    /// The widget performs **no filesystem operation** on its own.
93    /// Applications observe this event, perform whatever action
94    /// they wish (move, copy, symlink, upload, ignore), and
95    /// re-scan affected folders by emitting `Toggled` events
96    /// (collapse then re-expand) to refresh the tree view.
97    ///
98    /// `destination` is guaranteed to be a directory that is not
99    /// itself in `sources` nor a descendant of any source — see
100    /// [`DragMsg`](crate::DragMsg) for the validity rules.
101    /// `sources` is non-empty.
102    DragCompleted {
103        /// One or more paths the user started dragging. This is
104        /// the [selected set](crate::DirectoryTree::selected_paths)
105        /// at drag start if the pressed row was in the selection,
106        /// otherwise just the pressed row.
107        sources: Vec<PathBuf>,
108        /// The folder over which the user released the mouse.
109        destination: PathBuf,
110    },
111
112    /// Internal: an asynchronous scan completed.
113    ///
114    /// Parent applications should not construct this variant themselves;
115    /// it is produced by `iced::Task`s that [`DirectoryTree::update`]
116    /// returns and is routed back to `update` through the app's message
117    /// plumbing. Treat it as opaque.
118    ///
119    /// [`DirectoryTree::update`]: crate::DirectoryTree::update
120    Loaded(LoadPayload),
121}
122
123/// The payload of [`DirectoryTreeEvent::Loaded`].
124///
125/// The fields are crate-private so the internal representation can
126/// evolve without breaking callers — `Clone` / `Debug` are sufficient
127/// for anything a parent application needs to do with the message.
128#[derive(Debug, Clone)]
129pub struct LoadPayload {
130    /// Directory whose scan completed.
131    pub(crate) path: PathBuf,
132    /// Generation counter snapshot taken when the scan was issued.
133    /// Used to drop stale results if the user has since collapsed and
134    /// re-expanded the folder.
135    pub(crate) generation: u64,
136    /// Depth of `path` relative to the tree's root — kept here so the
137    /// update layer doesn't have to re-walk the tree to find it, and
138    /// reserved for future per-depth UI feedback (e.g. showing a
139    /// "reached max depth" indicator on the triggering node).
140    #[allow(dead_code)]
141    pub(crate) depth: u32,
142    /// The scan outcome.
143    ///
144    /// `Arc` keeps the message cheaply cloneable even when the entry
145    /// vector is large — iced passes messages by value and clones them
146    /// en route to subscribers.
147    pub(crate) result: Arc<Result<Vec<LoadedEntry>, crate::Error>>,
148}