use crate::event::{KeyEvent, KeyKind, MouseButton, MouseEvent, MouseKind};
use crate::geometry::Rect;
use crate::keymap::{Key, KeyChord, Modifiers};
use crate::widget::EventCx;
use super::super::click::{Click, LastPress};
use super::super::rows::{RowScroll, Step};
use super::super::select_box::SelectBox;
use super::{Flat, Tree};
#[derive(Debug, Default)]
struct Anchor(Option<String>);
#[derive(Debug, Default)]
struct Presses(LastPress<String>);
#[derive(Debug, Default)]
pub(super) struct TreeBox(Option<SelectBox<String>>);
impl TreeBox {
pub(super) fn drawn(&self) -> Option<Rect> {
self.0.as_ref().map(SelectBox::rect)
}
}
impl<Msg: 'static> Tree<Msg> {
pub(super) fn double_press(&self, cx: &mut EventCx<'_, Msg>, key: &str) -> bool {
if self.activate_on != Click::Double {
return false;
}
let now = cx.now();
cx.memory::<Presses>().0.press(key.to_owned(), now)
}
pub(super) fn forget_press(&self, cx: &mut EventCx<'_, Msg>) {
cx.memory::<Presses>().0.forget();
}
fn covered(cx: &mut EventCx<'_, Msg>, flat: &[Flat<'_>], rect: Rect) -> Vec<String> {
let area = cx.area();
let offset = cx.memory::<RowScroll>().offset;
(rect.y.max(area.y)..rect.bottom().min(area.bottom()))
.filter_map(|y| usize::try_from(y - area.y).ok())
.filter_map(|row| flat.get(offset + row))
.map(|row| row.node.key.clone())
.collect()
}
pub(super) fn box_pointer(
&self,
cx: &mut EventCx<'_, Msg>,
mouse: &MouseEvent,
flat: &[Flat<'_>],
index: Option<usize>,
) -> Option<bool> {
let at = (mouse.x, mouse.y);
let drawn = match mouse.kind {
MouseKind::Down(MouseButton::Left) => {
let free = Self::rows_area(cx.area(), flat.len()).contains(mouse.x, mouse.y) && index.is_none();
if !self.box_select || !self.is_multi() || !free {
return None;
}
self.forget_press(cx);
cx.capture_pointer();
SelectBox::new(at, mouse.mods.ctrl && !mouse.mods.alt, &self.chosen)
}
MouseKind::Drag(MouseButton::Left) | MouseKind::Up(MouseButton::Left) => {
let mut drawn = cx.memory::<TreeBox>().0.take()?;
drawn.stretch(at);
drawn
}
_ => return None,
};
let covered = Self::covered(cx, flat, drawn.rect());
self.choose(cx, drawn.selection(covered));
if mouse.kind != MouseKind::Up(MouseButton::Left) {
cx.memory::<TreeBox>().0 = Some(drawn);
}
Some(true)
}
pub(super) fn is_multi(&self) -> bool {
self.on_choose.is_some()
}
pub(super) fn is_chosen(&self, key: &str) -> bool {
if self.is_multi() {
self.chosen.iter().any(|chosen| chosen == key)
} else {
self.selected.as_deref() == Some(key)
}
}
pub(super) fn is_among_many(&self, key: &str) -> bool {
self.is_multi() && self.chosen.len() > 1 && self.is_chosen(key)
}
pub(super) fn choose(&self, cx: &mut EventCx<'_, Msg>, keys: Vec<String>) {
if let Some(message) = &self.on_choose
&& keys != self.chosen
{
cx.emit(message(keys));
}
}
pub(super) fn select_one(&self, cx: &mut EventCx<'_, Msg>, flat: &[Flat<'_>], index: usize) {
let Some(row) = flat.get(index) else { return };
self.select(cx, flat, index);
if self.is_multi() {
let key = row.node.key.clone();
cx.memory::<Anchor>().0 = Some(key.clone());
self.choose(cx, vec![key]);
}
}
fn toggle(&self, cx: &mut EventCx<'_, Msg>, flat: &[Flat<'_>], index: usize) {
let Some(row) = flat.get(index) else { return };
let key = &row.node.key;
let mut keys = self.chosen.clone();
match keys.iter().position(|chosen| chosen == key) {
Some(at) => {
keys.remove(at);
}
None => keys.push(key.clone()),
}
self.select(cx, flat, index);
cx.memory::<Anchor>().0 = Some(key.clone());
self.choose(cx, keys);
}
fn select_range(&self, cx: &mut EventCx<'_, Msg>, flat: &[Flat<'_>], index: usize) {
if index >= flat.len() {
return;
}
let anchor = cx.memory::<Anchor>().0.clone();
let start = anchor
.and_then(|key| flat.iter().position(|row| row.node.key == key))
.or_else(|| self.selected_index(flat))
.unwrap_or(index);
let (low, high) = (start.min(index), start.max(index));
let keys = flat[low..=high].iter().map(|row| row.node.key.clone()).collect();
if cx.memory::<Anchor>().0.is_none() {
cx.memory::<Anchor>().0 = Some(flat[start].node.key.clone());
}
self.select(cx, flat, index);
self.choose(cx, keys);
}
pub(super) fn modified_press(
&self,
cx: &mut EventCx<'_, Msg>,
flat: &[Flat<'_>],
index: usize,
mods: Modifiers,
) -> bool {
if !self.is_multi() || mods.alt {
return false;
}
if mods.ctrl != mods.shift {
self.forget_press(cx);
}
match (mods.ctrl, mods.shift) {
(true, false) => self.toggle(cx, flat, index),
(false, true) => self.select_range(cx, flat, index),
_ => return false,
}
true
}
pub(super) fn selection_key(&self, cx: &mut EventCx<'_, Msg>, key: &KeyEvent, flat: &[Flat<'_>]) -> bool {
if !self.is_multi() || key.kind == KeyKind::Release {
return false;
}
let current = self.selected_index(flat);
let shifted = Modifiers { shift: true, ..Modifiers::default() };
let steps = [Key::Up, Key::Down, Key::PageUp, Key::PageDown, Key::Home, Key::End];
if key.chord.mods == shifted && steps.contains(&key.chord.key) {
let plain = KeyEvent { chord: KeyChord { mods: Modifiers::default(), ..key.chord }, ..*key };
let page = usize::from(cx.area().height);
if let Some(target) = Step::from_key(&plain).and_then(|step| step.apply(current, flat.len(), page)) {
self.select_range(cx, flat, target);
}
return true;
}
if key.is_plain(Key::Space) {
return current.is_some_and(|index| {
self.toggle(cx, flat, index);
true
});
}
if key.is_plain(Key::Esc) && self.chosen.len() > 1 {
let Some(index) = current else { return false };
self.select_one(cx, flat, index);
return true;
}
false
}
}