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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Mouse-drag text selection — `mousedown` begins, `mousemove`
//! extends focus, `mouseup` ends.
//!
//! Called from `runtime/router/mouse/mod.rs` as default actions
//! on the mouse pipeline:
//!
//! - `begin`: tries to start a drag on `mousedown`. Returns true
//! when the click landed on selectable text and a drag is now
//! active — caller relies on pointer capture (which `begin` sets)
//! to route subsequent `mousemove`/`mouseup` back here.
//! - `extend`: on `mousemove` while a drag is active, moves the
//! selection's `focus` to the cursor's current position. Preserves
//! the original anchor so dragging backward shrinks the selection
//! symmetrically.
//! - `end`: clears router drag state. Pointer capture is released
//! by the router's own `handle_up` (browser-faithful auto-release).
//!
//! ## Why pointer capture
//!
//! Without capture, dragging off the original paragraph routes
//! subsequent moves to whatever chrome happens to sit underneath —
//! selection would "jump" or freeze. Holding capture on the IFC
//! block means every move comes back to us while the button is
//! down. Matches browser `setPointerCapture` semantics.
//!
//! ## What "selectable" means here
//!
//! A click is selectable iff `dom.position_at(x, y)` returns
//! `Some(_)`. That function already walks to the innermost IFC
//! block and rejects `user-select: none` subtrees, so this file
//! doesn't duplicate those checks.
use MouseEvent;
use Selection;
use crateTuiDom;
use crateUserSelect;
use crateis_descendant_or_self;
use crateinline_flow_for_text;
use crateHitTestExt;
use crateRouter;
use crateuser_select;
/// Default action for `mousedown`: begin a drag-select if the
/// click landed on selectable text.
///
/// Returns `true` when a drag was started. The router uses this
/// only to keep symmetry with other default actions — the real
/// "we're dragging" signal for follow-up moves is
/// `router.selection_drag.is_some()`, set by this function.
pub
/// Default action for `mousemove` (while `router.selection_drag` is
/// set): extend the selection's focus to the cursor's current
/// position. Returns `true` when the selection actually changed —
/// caller uses it to request a redraw.
///
/// When the cursor moves outside any selectable text (onto a
/// `user-select: none` bar, into a gap, or past end-of-line) the focus
/// snaps to the nearest selectable position to the pointer via
/// [`HitTestExt::nearest_selectable_position`] — browsers extend the
/// selection past such regions rather than freezing or collapsing it.
pub
// `clamp_to_anchor_flow` retired: the no-position fallback now snaps to the
// nearest *selectable* flow to the pointer via
// `HitTestExt::nearest_selectable_position` (so dragging over a
// `user-select: none` bar extends past it instead of collapsing back into the
// anchor flow). The y-overshoot clamp it used lives in `position_at`'s
// `clamp_to_line_layout`, shared by both the contained and nearest paths.
/// Clear router drag state. Call from `mouseup` regardless of
/// whether the up landed on text — the pointer capture is what
/// kept the drag alive, and it's auto-released by the router.
pub
// `ifc_block_of` retired — drag now stores `InlineFlow` directly,
// resolved via `inline_flow_for_text`. The pointer-capture holder
// falls back to `InlineFlow::owner()` (the container node id).