use std::path::PathBuf;
use crate::DirectoryTree;
use crate::drag::DragMsg;
use crate::event::DirectoryTreeEvent;
use crate::selection::SelectionMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TreeKey {
Up,
Down,
Home,
End,
Enter,
Space,
Left,
Right,
Escape,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Modifiers {
pub shift: bool,
pub ctrl: bool,
}
pub fn handle_key(
tree: &DirectoryTree,
key: TreeKey,
modifiers: Modifiers,
) -> Option<DirectoryTreeEvent> {
let rows = tree.visible_rows();
let active_idx: Option<usize> = tree
.selected_path()
.and_then(|active| rows.iter().position(|(node, _)| node.path == active));
match (key, modifiers.shift) {
(TreeKey::Up, false) => {
let idx = active_idx?.checked_sub(1)?;
let (node, _) = &rows[idx];
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::Replace,
))
}
(TreeKey::Up, true) => {
let idx = active_idx?.checked_sub(1)?;
let (node, _) = &rows[idx];
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::ExtendRange,
))
}
(TreeKey::Down, false) => {
let idx = active_idx? + 1;
let (node, _) = rows.get(idx)?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::Replace,
))
}
(TreeKey::Down, true) => {
let idx = active_idx? + 1;
let (node, _) = rows.get(idx)?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::ExtendRange,
))
}
(TreeKey::Home, false) => {
let (node, _) = rows.first()?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::Replace,
))
}
(TreeKey::Home, true) => {
let (node, _) = rows.first()?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::ExtendRange,
))
}
(TreeKey::End, false) => {
let (node, _) = rows.last()?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::Replace,
))
}
(TreeKey::End, true) => {
let (node, _) = rows.last()?;
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::ExtendRange,
))
}
(TreeKey::Enter, _) => {
let idx = active_idx?;
let (node, _) = &rows[idx];
if !node.is_dir {
return None;
}
Some(DirectoryTreeEvent::Toggled(node.path.clone()))
}
(TreeKey::Space, _) => {
let idx = active_idx?;
let (node, _) = &rows[idx];
Some(selected(
node.path.clone(),
node.is_dir,
SelectionMode::Toggle,
))
}
(TreeKey::Left, _) => {
let idx = active_idx?;
let (node, _) = &rows[idx];
if node.is_dir && node.is_expanded {
Some(DirectoryTreeEvent::Toggled(node.path.clone()))
} else {
if node.path == tree.config().root_path {
return None;
}
let parent: PathBuf = node.path.parent()?.to_path_buf();
Some(selected(parent, true, SelectionMode::Replace))
}
}
(TreeKey::Right, _) => {
let idx = active_idx?;
let (node, _) = &rows[idx];
if !node.is_dir {
return None;
}
if !node.is_expanded {
Some(DirectoryTreeEvent::Toggled(node.path.clone()))
} else {
let next_idx = idx + 1;
let (next_node, _) = rows.get(next_idx)?;
if next_node.path.parent() != Some(node.path.as_path()) {
return None; }
Some(selected(
next_node.path.clone(),
next_node.is_dir,
SelectionMode::Replace,
))
}
}
(TreeKey::Escape, _) => {
if tree.drag_state().is_some() {
Some(DirectoryTreeEvent::Drag(DragMsg::Cancelled))
} else {
None
}
}
}
}
#[inline]
fn selected(path: PathBuf, is_dir: bool, mode: SelectionMode) -> DirectoryTreeEvent {
DirectoryTreeEvent::Selected { path, is_dir, mode }
}