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
//! 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 press resolved to selectable text and a drag is now
//! active — recorded in `router.selection_drag`.
//! - `extend`: on a button-held `mousemove` while a drag is active,
//! moves the selection's `focus` to the cursor's current position,
//! wherever the pointer is. Preserves the original anchor so
//! dragging backward shrinks the selection symmetrically.
//! - `end`: clears router drag state (on `mouseup`, on a
//! button-less move that shows the `mouseup` was lost, and at the
//! start of every `mousedown`).
//!
//! ## No pointer capture
//!
//! A browser does not capture the pointer for a text-selection drag:
//! during it `mousemove` / `mouseup` target whatever is under the
//! pointer, and `click` goes to the common ancestor of the mousedown
//! and mouseup targets (UI Events). So the drag lives in router-private
//! state, not in `Dom::pointer_capture` — the router extends the
//! selection on every button-held move regardless of the hit target,
//! and the event targeting stays untouched. (Taking capture here once
//! retargeted the `click` of any widget beside prose to the prose
//! container: P6G-SELECTION-CAPTURE-1.) Edge autoscroll keys on the
//! same router state (`App::note_autoscroll`).
//!
//! ## 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 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 "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, from a button-less move (the
/// `mouseup` was lost outside the terminal), and at the start of every
/// `mousedown` (a lost `mouseup` with no button-less motion reported).
pub