1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! v0.4: drag-and-drop state machine.
//!
//! The widget tracks an in-flight drag through a small state
//! machine and emits a [`DragCompleted`](crate::DirectoryTreeEvent::DragCompleted)
//! event when the user releases over a valid drop target. The
//! widget itself never touches the filesystem — the application is
//! responsible for actually moving / copying / symlinking / etc. the
//! dragged paths. This keeps the widget a pure UI layer that works
//! equally well for local files, network shares, zip archives, or
//! any other hierarchical backend the app exposes.
//!
//! # Valid drop target
//!
//! A path `T` is a valid drop target for a drag carrying source set
//! `S = {s1, s2, ...}` iff:
//!
//! 1. `T` is a directory — you can't drop into a file.
//! 2. `T` is not itself a member of `S` — dropping A onto A is a no-op.
//! 3. `T` is not a descendant of any member of `S` — dropping
//! `/foo` onto `/foo/bar` would create a circular move.
//!
//! Validity is recomputed on every [`DragMsg::Entered`] event; the
//! view reads `DragState::hover` to paint a highlight on the current
//! drop target.
//!
//! # Deferred selection
//!
//! Because drags start with a mouse-down on a row and the same
//! mouse-down would otherwise collapse a multi-selection to that
//! single row (via v0.3's `SelectionMode::Replace`), the widget
//! uses the standard "deferred selection" pattern: the view emits
//! [`DragMsg::Pressed`] on mouse-down — which does **not** change
//! the selection — and [`DragMsg::Released`] on mouse-up. If the
//! release is on the same row as the press (i.e., the gesture was
//! a click, not a drag), the widget dispatches a
//! [`Selected(_, _, SelectionMode::Replace)`](crate::DirectoryTreeEvent::Selected)
//! event at that point. This matches Windows Explorer / macOS
//! Finder / VS Code behaviour: clicking an already-selected item
//! doesn't clobber the multi-selection until the user lets go
//! without having moved.
use ;
/// Opaque drag-machinery event produced by the widget's internal
/// mouse-area instrumentation.
///
/// Applications should treat these as opaque payloads and route
/// them back to [`DirectoryTree::update`](crate::DirectoryTree::update)
/// unchanged — exactly like
/// [`LoadPayload`](crate::LoadPayload). Apps generally never
/// construct these variants by hand.
/// In-progress drag state. Crate-internal — held on
/// [`DirectoryTree`](crate::DirectoryTree) and mutated by the
/// update layer.
pub