use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::cache::TreeCache;
use crate::config::DisplayFilter;
use crate::entry::LoadedEntry;
use crate::node::TreeNode;
use crate::scan::{LoadPayload, LoadedOutcome, ScanRequest};
use crate::selection::{self, SelectionMode};
use crate::tree::DirectoryTree;
impl DirectoryTree {
pub fn on_toggled(&mut self, path: &Path) -> Option<ScanRequest> {
let depth = self.depth_of(path)?;
let max_depth = self.config.max_depth;
let node = self.root.find_mut(path)?;
if !node.is_dir {
return None;
}
if node.is_expanded {
node.is_expanded = false;
return None;
}
if node.is_loaded {
node.is_expanded = true;
return None;
}
if let Some(max) = max_depth
&& depth > max
{
node.is_loaded = true;
node.is_expanded = true;
node.children.clear();
return None;
}
node.is_expanded = true;
self.generation = self.generation.wrapping_add(1);
Some(ScanRequest {
path: path.to_path_buf(),
generation: self.generation,
depth,
})
}
pub fn on_loaded(&mut self, payload: LoadPayload) -> LoadedOutcome {
if payload.generation != self.generation {
return LoadedOutcome::discarded();
}
let filter = self.config.filter;
let Some(node) = self.root.find_mut(&payload.path) else {
return LoadedOutcome::discarded();
};
match payload.result {
Ok(entries) => {
rebuild_children(node, &entries, filter);
node.error = None;
node.is_loaded = true;
self.cache.insert(payload.path, payload.generation, entries);
}
Err(issue) => {
node.children.clear();
node.error = Some(issue);
node.is_loaded = true;
}
}
selection::sync_flags(&mut self.root, &self.selected_paths);
LoadedOutcome::accepted()
}
pub fn on_selected(&mut self, path: &Path, _is_dir: bool, mode: SelectionMode) {
let path = path.to_path_buf();
self.active_path = Some(path.clone());
match mode {
SelectionMode::Replace => {
self.selected_paths = vec![path.clone()];
self.anchor_path = Some(path);
}
SelectionMode::Toggle => {
if let Some(pos) = self.selected_paths.iter().position(|p| p == &path) {
self.selected_paths.remove(pos);
} else {
self.selected_paths.push(path.clone());
}
self.anchor_path = Some(path);
}
SelectionMode::ExtendRange => {
let Some(anchor) = self.anchor_path.clone() else {
self.selected_paths = vec![path.clone()];
self.anchor_path = Some(path);
selection::sync_flags(&mut self.root, &self.selected_paths);
return;
};
let rows = self.visible_rows();
let anchor_idx = rows.iter().position(|(n, _)| n.path == anchor);
let target_idx = rows.iter().position(|(n, _)| n.path == path);
if let (Some(a), Some(t)) = (anchor_idx, target_idx) {
let (lo, hi) = if a <= t { (a, t) } else { (t, a) };
self.selected_paths =
rows[lo..=hi].iter().map(|(n, _)| n.path.clone()).collect();
}
}
}
selection::sync_flags(&mut self.root, &self.selected_paths);
}
}
pub(crate) fn rebuild_children(
node: &mut TreeNode,
entries: &[LoadedEntry],
filter: DisplayFilter,
) {
let mut previous: HashMap<PathBuf, TreeNode> = std::mem::take(&mut node.children)
.into_iter()
.map(|child| (child.path.clone(), child))
.collect();
node.children = entries
.iter()
.filter(|entry| filter.admits(entry))
.map(|entry| {
previous
.remove(&entry.path)
.unwrap_or_else(|| TreeNode::from_entry(entry))
})
.collect();
}
pub(crate) fn refresh_from_cache(node: &mut TreeNode, cache: &TreeCache, filter: DisplayFilter) {
if node.is_dir
&& node.is_loaded
&& node.error.is_none()
&& let Some(cached) = cache.get(&node.path)
{
rebuild_children(node, &cached.entries, filter);
}
for child in &mut node.children {
refresh_from_cache(child, cache, filter);
}
}