1use crate::event::{Event, KeyEvent, MouseButton, MouseEvent, MouseKind};
10use crate::geometry::Rect;
11use crate::keymap::Key;
12use crate::widget::{EventCx, PaintCx, Widget};
13
14use super::super::TabEdit;
15use super::super::context_item;
16use super::super::context_menu::{self, ContextMenu};
17use super::super::edge_scroll::{Edge, EdgeScroll, Zone};
18use super::super::rows::RowScroll;
19use super::super::tab_model::{Direction, drop_target};
20use super::drop::{Aim, Spring};
21use super::{Flat, Tree, TreeNode};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct TreeMove {
28 pub key: String,
30 pub parent: Option<String>,
32 pub from: usize,
34 pub to: usize,
36}
37
38impl TreeMove {
39 pub fn apply<T>(&self, siblings: &mut Vec<T>) {
43 let mut unused = 0;
44 TabEdit::Move { from: self.from, to: self.to }.apply(siblings, &mut unused);
45 }
46}
47
48pub(super) struct Arrange<'k> {
52 pub(super) key: &'k str,
53 pub(super) parent: Option<&'k str>,
54 pub(super) order: Option<(usize, usize)>,
55}
56
57#[derive(Debug, Clone)]
59struct Press {
60 key: String,
61 keys: Vec<String>,
63 start: (i32, i32),
64 pointer: (i32, i32),
65 dragging: bool,
66 reduce: bool,
69}
70
71#[derive(Debug, Default)]
73struct TreePointer {
74 pressed: Option<Press>,
75 edge: EdgeScroll,
77}
78
79pub(super) struct Drag {
81 pub(super) key: String,
83 pub(super) keys: Vec<String>,
85 pub(super) pointer: (i32, i32),
86}
87
88#[derive(Debug, Default)]
90struct MenuNode(Option<String>);
91
92impl<Msg: 'static> Tree<Msg> {
93 pub(super) fn siblings(&self, key: &str) -> Option<(Option<&str>, &[TreeNode])> {
95 fn find<'a>(
96 nodes: &'a [TreeNode],
97 parent: Option<&'a str>,
98 key: &str,
99 ) -> Option<(Option<&'a str>, &'a [TreeNode])> {
100 if nodes.iter().any(|node| node.key == key) {
101 return Some((parent, nodes));
102 }
103 nodes.iter().find_map(|node| find(&node.children, Some(&node.key), key))
104 }
105 find(&self.roots, None, key)
106 }
107
108 pub(super) fn move_node(&self, cx: &mut EventCx<'_, Msg>, key: &str, parent: Option<&str>, from: usize, to: usize) {
110 if from != to
111 && let Some(message) = &self.on_move
112 {
113 cx.emit(message(TreeMove { key: key.to_owned(), parent: parent.map(str::to_owned), from, to }));
114 }
115 }
116
117 pub(super) fn move_key(&self, cx: &mut EventCx<'_, Msg>, key: &KeyEvent) -> bool {
119 let mods = key.chord.mods;
120 let up = key.chord.key == Key::Up;
121 if self.on_move.is_none() || !mods.ctrl || !mods.shift || mods.alt || !(up || key.chord.key == Key::Down) {
122 return false;
123 }
124 let Some(selected) = self.selected.as_deref() else {
125 return true;
126 };
127 let Some((parent, siblings)) = self.siblings(selected) else {
128 return true;
129 };
130 let Some(from) = siblings.iter().position(|node| node.key == selected) else {
131 return true;
132 };
133 let to = if up { from.saturating_sub(1) } else { (from + 1).min(siblings.len() - 1) };
134 self.move_node(cx, selected, parent, from, to);
135 true
136 }
137
138 pub(super) fn resting(&self, key: &str) -> Vec<Flat<'_>> {
140 let parent = self.siblings(key).and_then(|(parent, _)| parent);
141 self.flatten_with(Some(&Arrange { key, parent, order: None }))
142 }
143
144 pub(super) fn landing(&self, key: &str, pointer: (i32, i32), area: Rect, offset: usize) -> Option<(usize, usize)> {
148 let flat = self.resting(key);
149 let row = flat.iter().position(|row| row.node.key == key)?;
150 let parent = flat[row].parent;
151 let from = flat[row].index;
152 let visible = usize::from(area.height);
153 let slots: Vec<(usize, Rect)> = flat
154 .iter()
155 .enumerate()
156 .skip(offset)
157 .take(visible)
158 .filter(|(_, other)| other.parent == parent)
159 .map(|(at, other)| {
160 let y = area.y + i32::try_from(at - offset).unwrap_or(0);
161 (other.index, Rect::new(area.x, y, area.width, 1))
162 })
163 .collect();
164 Some((from, drop_target(&slots, from, pointer, Direction::Down)))
165 }
166
167 pub(super) fn drag(&self, cx: &mut PaintCx<'_>) -> Option<Drag> {
169 if self.on_move.is_none() && self.dropping.is_none() {
170 return None;
171 }
172 let press = cx.memory::<TreePointer>().pressed.clone()?;
173 (press.dragging && self.siblings(&press.key).is_some()).then_some(Drag {
174 key: press.key,
175 keys: press.keys,
176 pointer: press.pointer,
177 })
178 }
179
180 pub(super) fn drag_pointer(
186 &self,
187 cx: &mut EventCx<'_, Msg>,
188 mouse: &MouseEvent,
189 flat: &[Flat<'_>],
190 index: Option<usize>,
191 ) -> Option<bool> {
192 let at = (mouse.x, mouse.y);
193 match mouse.kind {
194 MouseKind::Down(MouseButton::Left) => {
195 let index = index?;
196 if self.modified_press(cx, flat, index, mouse.mods) {
197 return Some(true);
198 }
199 let key = flat[index].node.key.clone();
200 let reduce = self.is_among_many(&key);
203 if reduce {
204 self.select(cx, flat, index);
205 } else {
206 self.select_one(cx, flat, index);
207 }
208 cx.capture_pointer();
209 *cx.memory::<Spring>() = Spring::default();
210 let keys = self.carried(&key);
211 let press = Press { key, keys, start: at, pointer: at, dragging: false, reduce };
212 *cx.memory::<TreePointer>() = TreePointer { pressed: Some(press), edge: EdgeScroll::default() };
213 Some(true)
214 }
215 MouseKind::Drag(MouseButton::Left) => {
216 let memory = cx.memory::<TreePointer>();
217 let press = memory.pressed.as_mut()?;
218 press.pointer = at;
219 let starts = !press.dragging && (at.1 - press.start.1).abs() >= 1;
220 if starts {
221 press.dragging = true;
222 }
223 let dragging = press.dragging;
224 let keys = press.keys.clone();
225 if starts && press.reduce && self.dropping.is_none() {
227 press.reduce = false;
228 self.choose(cx, keys.clone());
229 }
230 self.edge_scroll(cx, mouse.y, flat.len(), dragging);
231 if dragging && self.dropping.is_some() {
232 let offset = cx.memory::<RowScroll>().offset;
233 let aim = self.aim(&keys, at, Self::rows_area(cx.area(), flat.len()), offset);
234 self.wake_for_spring(cx, &aim);
235 }
236 Some(true)
237 }
238 MouseKind::Up(MouseButton::Left) => {
239 let memory = cx.memory::<TreePointer>();
240 memory.edge = EdgeScroll::default();
241 let press = memory.pressed.take()?;
242 let area = cx.area();
243 *cx.memory::<Spring>() = Spring::default();
244 if press.dragging {
245 let offset = cx.memory::<RowScroll>().offset;
246 let aim = self.aim(&press.keys, at, Self::rows_area(area, flat.len()), offset);
247 self.release(cx, press.keys, aim);
248 } else if let Some(row) = flat.iter().position(|row| row.node.key == press.key) {
249 if press.reduce {
250 self.select_one(cx, flat, row);
251 }
252 self.open_or_activate(cx, row, flat[row].node);
253 }
254 Some(true)
255 }
256 _ => None,
257 }
258 }
259
260 fn wake_for_spring(&self, cx: &mut EventCx<'_, Msg>, aim: &Aim) {
263 let now = cx.now();
264 let edge = cx.memory::<TreePointer>().edge.due();
265 match (self.spring(cx, aim), edge) {
266 (Some(wait), Some(due)) => cx.repeat_pointer(wait.min(due.saturating_sub(now))),
267 (Some(wait), None) => cx.repeat_pointer(wait),
268 (None, Some(_)) => {}
270 (None, None) => cx.stop_pointer_repeat(),
271 }
272 }
273
274 pub(super) fn rows_area(area: Rect, total: usize) -> Rect {
276 let overflows = total > usize::from(area.height);
277 Rect::new(area.x, area.y, area.width.saturating_sub(u16::from(overflows)), area.height)
278 }
279
280 fn edge_scroll(&self, cx: &mut EventCx<'_, Msg>, y: i32, total: usize, dragging: bool) {
282 let area = cx.area();
283 let last = area.bottom() - 1;
284 let zone = if y <= area.y {
285 Some(Zone { edge: Edge::Back, beyond: u16::try_from(area.y - y).unwrap_or(u16::MAX) })
286 } else if y >= last {
287 Some(Zone { edge: Edge::Forward, beyond: u16::try_from(y - last).unwrap_or(u16::MAX) })
288 } else {
289 None
290 };
291 let visible = usize::from(area.height);
292 let mut edge = cx.memory::<TreePointer>().edge;
293 edge.drive(cx, zone.filter(|_| dragging), |cx, edge| {
294 let memory = cx.memory::<RowScroll>();
295 memory.offset = match edge {
296 Edge::Back => memory.offset.checked_sub(1)?,
297 Edge::Forward => Some(memory.offset + 1).filter(|next| *next + visible <= total)?,
298 };
299 Some(memory.offset)
300 });
301 cx.memory::<TreePointer>().edge = edge;
302 }
303
304 fn menu_for(&self, key: &str) -> Option<(ContextMenu<usize>, Vec<Msg>)> {
306 let items = self.menu.as_ref()?;
307 let (items, messages) = context_item::keyed(items(key));
308 Some((ContextMenu::new(items), messages))
309 }
310
311 fn open_menu(&self, cx: &mut EventCx<'_, Msg>, key: &str, anchor: Rect, keyboard: bool) {
312 let Some((menu, _)) = self.menu_for(key) else {
313 return;
314 };
315 cx.memory::<MenuNode>().0 = Some(key.to_owned());
316 cx.with_messages(|cx: &mut EventCx<'_, usize>| menu.open(cx, anchor, keyboard));
317 }
318
319 pub(super) fn menu_event(&self, cx: &mut EventCx<'_, Msg>, event: &Event, flat: &[Flat<'_>]) -> bool {
327 if self.menu.is_none() || flat.is_empty() {
328 return false;
329 }
330 let right_press = match event {
331 Event::Mouse(mouse) if mouse.kind == MouseKind::Down(MouseButton::Right) => Some((mouse.x, mouse.y)),
332 _ => None,
333 };
334 if context_menu::is_open_in(cx) {
335 let node = cx.memory::<MenuNode>().0.clone().filter(|key| flat.iter().any(|row| row.node.key == *key));
336 let elsewhere = right_press.is_some_and(|(x, y)| !context_menu::contains(cx, x, y));
337 match node.and_then(|key| self.menu_for(&key)).filter(|_| !elsewhere) {
340 Some((menu, messages)) => {
341 let (used, chosen) = cx.with_messages(|cx| menu.event(cx, event));
342 if let Some(message) = chosen.last().and_then(|chosen| messages.into_iter().nth(*chosen)) {
343 cx.emit(message);
344 }
345 return used;
346 }
347 None => {
348 cx.with_messages(|cx: &mut EventCx<'_, usize>| ContextMenu::<usize>::close(cx));
349 }
350 }
351 }
352 let area = cx.area();
353 let visible = usize::from(area.height);
354 match event {
355 Event::Mouse(_) => {
356 let Some((x, y)) = right_press else {
357 return false;
358 };
359 let offset = cx.memory::<RowScroll>().offset;
360 let row = usize::try_from(y - area.y).ok().map(|r| offset + r).filter(|i| *i < flat.len());
361 let Some(row) = row.filter(|_| x < Self::rows_area(area, flat.len()).right()) else {
362 return false;
363 };
364 if self.is_multi() && !self.is_chosen(&flat[row].node.key) {
368 self.select_one(cx, flat, row);
369 }
370 self.open_menu(cx, &flat[row].node.key, Rect::new(x, y, 1, 1), false);
371 true
372 }
373 Event::Key(key) if context_menu::is_menu_key(key) => {
374 let Some(row) = self.selected_index(flat) else {
375 return false;
376 };
377 let memory = cx.memory::<RowScroll>();
378 if row < memory.offset {
379 memory.offset = row;
380 } else if visible > 0 && row >= memory.offset + visible {
381 memory.offset = row + 1 - visible;
382 }
383 let y = area.y + i32::try_from(row - memory.offset).unwrap_or(0);
384 let anchor = Rect::new(area.x, y, Self::rows_area(area, flat.len()).width, 1);
385 self.open_menu(cx, &flat[row].node.key, anchor, true);
386 true
387 }
388 _ => false,
389 }
390 }
391
392 pub(super) fn menu_node(&self, cx: &mut PaintCx<'_>) -> Option<String> {
395 if self.menu.is_none() || !context_menu::is_open(cx) {
396 return None;
397 }
398 cx.memory::<MenuNode>().0.clone()
399 }
400
401 pub(super) fn paint_menu(&self, cx: &mut PaintCx<'_>, anchor: Rect) {
403 if let Some((menu, _)) = self.menu_node(cx).and_then(|key| self.menu_for(&key)) {
404 menu.paint_overlay(cx, anchor);
405 }
406 }
407}