1use crate::event::{KeyEvent, KeyKind, MouseButton, MouseEvent, MouseKind};
11use crate::geometry::Rect;
12use crate::keymap::{Key, KeyChord, Modifiers};
13use crate::widget::{EventCx, PaintCx};
14
15use super::click::{Click, LastPress};
16use super::select_box::SelectBox;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct RowDrop {
23 pub rows: Vec<usize>,
26 pub into: usize,
28}
29
30type ChooseMessage<Msg> = Box<dyn Fn(Vec<usize>) -> Msg>;
32
33type DropMessage<Msg> = Box<dyn Fn(RowDrop) -> Msg>;
35
36type DropFilter = Box<dyn Fn(usize) -> bool>;
38
39pub(crate) enum Spot {
41 Row(usize),
43 Free,
45 Outside,
47}
48
49pub(crate) trait PickedRows<Msg> {
52 fn spot(&self, cx: &mut EventCx<'_, Msg>, x: i32, y: i32) -> Spot;
54 fn covered(&self, cx: &mut EventCx<'_, Msg>, rect: Rect) -> Vec<usize>;
56 fn cursor(&self) -> Option<usize>;
58 fn select(&self, cx: &mut EventCx<'_, Msg>, index: usize);
60 fn open(&self, cx: &mut EventCx<'_, Msg>, index: usize, at: (i32, i32));
62}
63
64pub(crate) struct Picking<Msg> {
67 pub(crate) activate_on: Click,
69 pub(crate) chosen: Vec<usize>,
71 pub(crate) on_choose: Option<ChooseMessage<Msg>>,
73 pub(crate) box_select: bool,
75 pub(crate) dropping: Option<(DropMessage<Msg>, DropFilter)>,
77 pub(crate) copy_drop: Option<DropMessage<Msg>>,
79}
80
81impl<Msg> Default for Picking<Msg> {
82 fn default() -> Self {
83 Self {
84 activate_on: Click::Single,
85 chosen: Vec::new(),
86 on_choose: None,
87 box_select: false,
88 dropping: None,
89 copy_drop: None,
90 }
91 }
92}
93
94#[derive(Debug, Clone)]
96struct RowPress {
97 index: usize,
98 start: (i32, i32),
99 pointer: (i32, i32),
100 dragging: bool,
101 carried: Vec<usize>,
103 reduce: bool,
106}
107
108#[derive(Debug, Clone)]
110enum Held {
111 Row(RowPress),
112 Box(SelectBox<usize>),
113}
114
115#[derive(Debug, Default)]
117struct PointerRows {
118 presses: LastPress<usize>,
119 held: Option<Held>,
120 anchor: Option<usize>,
122}
123
124impl<Msg: 'static> Picking<Msg> {
125 pub(crate) fn is_multi(&self) -> bool {
127 self.on_choose.is_some()
128 }
129
130 pub(crate) fn is_chosen(&self, index: usize) -> bool {
132 self.is_multi() && self.chosen.contains(&index)
133 }
134
135 fn choose(&self, cx: &mut EventCx<'_, Msg>, rows: Vec<usize>) {
137 if let Some(message) = &self.on_choose
138 && rows != self.chosen
139 {
140 cx.emit(message(rows));
141 }
142 }
143
144 pub(crate) fn select_one(&self, cx: &mut EventCx<'_, Msg>, rows: &impl PickedRows<Msg>, index: usize) {
146 rows.select(cx, index);
147 if self.is_multi() {
148 cx.memory::<PointerRows>().anchor = Some(index);
149 self.choose(cx, vec![index]);
150 }
151 }
152
153 pub(crate) fn toggle(&self, cx: &mut EventCx<'_, Msg>, rows: &impl PickedRows<Msg>, index: usize) -> bool {
156 if !self.is_multi() {
157 return false;
158 }
159 let mut chosen = self.chosen.clone();
160 match chosen.iter().position(|row| *row == index) {
161 Some(at) => {
162 chosen.remove(at);
163 }
164 None => chosen.push(index),
165 }
166 rows.select(cx, index);
167 cx.memory::<PointerRows>().anchor = Some(index);
168 self.choose(cx, chosen);
169 true
170 }
171
172 fn select_range(&self, cx: &mut EventCx<'_, Msg>, rows: &impl PickedRows<Msg>, index: usize) {
175 let start = cx.memory::<PointerRows>().anchor.or_else(|| rows.cursor()).unwrap_or(index);
176 cx.memory::<PointerRows>().anchor = Some(start);
177 rows.select(cx, index);
178 self.choose(cx, (start.min(index)..=start.max(index)).collect());
179 }
180
181 pub(crate) fn selection_key(
189 &self,
190 cx: &mut EventCx<'_, Msg>,
191 key: &KeyEvent,
192 rows: &impl PickedRows<Msg>,
193 count: usize,
194 step: impl FnOnce(&mut EventCx<'_, Msg>, &KeyEvent) -> Option<Option<usize>>,
195 ) -> bool {
196 if !self.is_multi() || key.kind == KeyKind::Release {
197 return false;
198 }
199 if key.chord == select_all() {
200 self.choose(cx, (0..count).collect());
201 return true;
202 }
203 if key.is_plain(Key::Esc) && self.chosen.len() > 1 {
204 let Some(index) = rows.cursor() else { return false };
205 self.select_one(cx, rows, index);
206 return true;
207 }
208 let shifted = Modifiers { shift: true, ..Modifiers::default() };
209 if key.chord.mods != shifted {
210 return false;
211 }
212 let plain = KeyEvent { chord: KeyChord { mods: Modifiers::default(), ..key.chord }, ..*key };
213 let Some(target) = step(cx, &plain) else { return false };
214 if let Some(index) = target {
215 self.select_range(cx, rows, index);
216 }
217 true
218 }
219
220 pub(crate) fn takes_drop(&self, carried: &[usize], into: usize) -> bool {
222 self.dropping.as_ref().is_some_and(|(_, accepts)| accepts(into)) && !carried.contains(&into)
223 }
224
225 pub(crate) fn mouse(
228 &self,
229 cx: &mut EventCx<'_, Msg>,
230 mouse: &MouseEvent,
231 rows: &impl PickedRows<Msg>,
232 ) -> Option<bool> {
233 match mouse.kind {
234 MouseKind::Down(MouseButton::Left) => self.press(cx, mouse, rows),
235 MouseKind::Drag(MouseButton::Left) => self.drag(cx, (mouse.x, mouse.y), rows),
236 MouseKind::Up(MouseButton::Left) => self.release(cx, mouse, rows),
237 _ => None,
238 }
239 }
240
241 fn press(&self, cx: &mut EventCx<'_, Msg>, mouse: &MouseEvent, rows: &impl PickedRows<Msg>) -> Option<bool> {
242 let at = (mouse.x, mouse.y);
243 let mods = mouse.mods;
244 let index = match rows.spot(cx, mouse.x, mouse.y) {
245 Spot::Outside => return None,
246 Spot::Free if self.box_select && self.is_multi() => {
247 let drawn = SelectBox::new(at, mods.ctrl && !mods.alt, &self.chosen);
248 let covered = rows.covered(cx, drawn.rect());
249 self.choose(cx, drawn.selection(covered));
250 let memory = cx.memory::<PointerRows>();
251 memory.presses.forget();
252 memory.held = Some(Held::Box(drawn));
253 cx.capture_pointer();
254 return Some(true);
255 }
256 Spot::Free => return None,
257 Spot::Row(index) => index,
258 };
259 if self.is_multi() && !mods.alt && mods.ctrl != mods.shift {
260 cx.memory::<PointerRows>().presses.forget();
261 if mods.ctrl {
262 self.toggle(cx, rows, index);
263 } else {
264 self.select_range(cx, rows, index);
265 }
266 return Some(true);
267 }
268 let now = cx.now();
269 if self.activate_on == Click::Double && cx.memory::<PointerRows>().presses.press(index, now) {
270 rows.select(cx, index);
271 rows.open(cx, index, at);
272 return Some(true);
273 }
274 let reduce = self.dropping.is_some() && self.chosen.len() > 1 && self.is_chosen(index);
277 if reduce {
278 rows.select(cx, index);
279 } else {
280 self.select_one(cx, rows, index);
281 }
282 if self.dropping.is_none() {
283 if self.activate_on == Click::Single {
284 rows.open(cx, index, at);
285 }
286 return Some(true);
287 }
288 let carried = if reduce { sorted(&self.chosen) } else { vec![index] };
289 let press = RowPress { index, start: at, pointer: at, dragging: false, carried, reduce };
290 cx.memory::<PointerRows>().held = Some(Held::Row(press));
291 cx.capture_pointer();
292 Some(true)
293 }
294
295 fn drag(&self, cx: &mut EventCx<'_, Msg>, at: (i32, i32), rows: &impl PickedRows<Msg>) -> Option<bool> {
296 let PointerRows { presses, held, .. } = cx.memory::<PointerRows>();
297 match held.as_mut()? {
298 Held::Box(drawn) => {
299 drawn.stretch(at);
300 let drawn = drawn.clone();
301 let covered = rows.covered(cx, drawn.rect());
302 self.choose(cx, drawn.selection(covered));
303 }
304 Held::Row(press) => {
305 press.pointer = at;
306 if !press.dragging && at != press.start {
307 press.dragging = true;
308 presses.forget();
310 }
311 }
312 }
313 Some(true)
314 }
315
316 fn release(&self, cx: &mut EventCx<'_, Msg>, mouse: &MouseEvent, rows: &impl PickedRows<Msg>) -> Option<bool> {
317 let at = (mouse.x, mouse.y);
318 match cx.memory::<PointerRows>().held.take()? {
319 Held::Box(mut drawn) => {
320 drawn.stretch(at);
321 let covered = rows.covered(cx, drawn.rect());
322 self.choose(cx, drawn.selection(covered));
323 }
324 Held::Row(press) if press.dragging => {
325 let Spot::Row(into) = rows.spot(cx, mouse.x, mouse.y) else { return Some(true) };
326 if !self.takes_drop(&press.carried, into) {
327 return Some(true);
328 }
329 let drop = RowDrop { rows: press.carried, into };
330 let message = match (&self.copy_drop, &self.dropping) {
332 (Some(copy), _) if mouse.mods.ctrl => copy(drop),
333 (_, Some((moving, _))) => moving(drop),
334 (_, None) => return Some(true),
335 };
336 cx.emit(message);
337 }
338 Held::Row(press) => {
339 if press.reduce {
340 self.select_one(cx, rows, press.index);
341 }
342 if self.activate_on == Click::Single {
343 rows.open(cx, press.index, at);
344 }
345 }
346 }
347 Some(true)
348 }
349}
350
351pub(crate) fn select_all() -> KeyChord {
353 KeyChord { key: Key::Char('a'), mods: Modifiers { ctrl: true, ..Modifiers::default() } }
354}
355
356fn sorted(rows: &[usize]) -> Vec<usize> {
358 let mut rows = rows.to_vec();
359 rows.sort_unstable();
360 rows
361}
362
363pub(crate) fn dragged(cx: &mut PaintCx<'_>) -> Option<((i32, i32), Vec<usize>)> {
365 match &cx.memory::<PointerRows>().held {
366 Some(Held::Row(press)) if press.dragging => Some((press.pointer, press.carried.clone())),
367 _ => None,
368 }
369}
370
371pub(crate) fn drawn_box(cx: &mut PaintCx<'_>) -> Option<Rect> {
373 match &cx.memory::<PointerRows>().held {
374 Some(Held::Box(drawn)) => Some(drawn.rect()),
375 _ => None,
376 }
377}