use std::collections::HashSet;
use std::path::{Path, PathBuf};
use iced::keyboard::{self, Modifiers};
use iced::widget::{Column, column, container, scrollable, text};
use iced::{Element, Length, Subscription, Task};
use iced_swdir_tree::{DirectoryFilter, DirectoryTree, DirectoryTreeEvent, SelectionMode};
#[derive(Debug, Clone)]
enum Message {
Tree(DirectoryTreeEvent),
ModifiersChanged(Modifiers),
Key(keyboard::Key, Modifiers),
}
struct App {
tree: DirectoryTree,
modifiers: Modifiers,
}
impl App {
fn new() -> (Self, Task<Message>) {
let root = std::env::args()
.nth(1)
.map(PathBuf::from)
.unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")));
let root_for_task = root.clone();
let tree = DirectoryTree::new(root).with_filter(DirectoryFilter::FilesAndFolders);
(
Self {
tree,
modifiers: Modifiers::default(),
},
Task::done(Message::Tree(DirectoryTreeEvent::Toggled(root_for_task))),
)
}
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Tree(DirectoryTreeEvent::Selected(path, is_dir, _from_view)) => {
let mode = SelectionMode::from_modifiers(self.modifiers);
let event = DirectoryTreeEvent::Selected(path, is_dir, mode);
self.tree.update(event).map(Message::Tree)
}
Message::Tree(event) => self.tree.update(event).map(Message::Tree),
Message::ModifiersChanged(m) => {
self.modifiers = m;
Task::none()
}
Message::Key(key, mods) => {
if let Some(event) = self.tree.handle_key(&key, mods) {
return self.tree.update(event).map(Message::Tree);
}
Task::none()
}
}
}
fn subscription(&self) -> Subscription<Message> {
keyboard::listen().map(|event| match event {
keyboard::Event::KeyPressed { key, modifiers, .. } => Message::Key(key, modifiers),
keyboard::Event::ModifiersChanged(modifiers) => Message::ModifiersChanged(modifiers),
_ => Message::Key(
keyboard::Key::Named(keyboard::key::Named::F35),
Modifiers::default(),
),
})
}
fn view(&self) -> Element<'_, Message> {
let selected = self.tree.selected_paths();
let count = selected.len();
let summary_text = if count == 0 {
"No selection. Click to select, Shift+click for range, \
Ctrl/Cmd+click to toggle."
.to_string()
} else {
format!(
"{count} selected (anchor: {})",
self.tree
.anchor_path()
.map(short_name)
.unwrap_or_else(|| "-".into())
)
};
const MAX_SHOWN: usize = 10;
let names: HashSet<String> = selected.iter().map(|p| short_name(p)).collect();
let mut names_sorted: Vec<String> = names.into_iter().collect();
names_sorted.sort();
let shown: String = if names_sorted.len() <= MAX_SHOWN {
names_sorted.join(", ")
} else {
format!(
"{}, +{} more",
names_sorted
.iter()
.take(MAX_SHOWN)
.cloned()
.collect::<Vec<_>>()
.join(", "),
names_sorted.len() - MAX_SHOWN
)
};
let status = Column::new()
.push(text(summary_text).size(13))
.push(text(shown).size(11))
.spacing(2);
container(
column![
scrollable(self.tree.view(Message::Tree)).height(Length::Fill),
status,
]
.spacing(8.0)
.padding(8.0),
)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
fn short_name(p: &Path) -> String {
p.file_name()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_else(|| p.display().to_string())
}
fn main() -> iced::Result {
iced::application(App::new, App::update, App::view)
.subscription(App::subscription)
.title("iced-swdir-tree · multi-select example")
.run()
}