use std::path::Path;
use iced::{
Alignment, Element, Length,
widget::{Space, button, column, container, row, scrollable, text},
};
use super::DirectoryTree;
use super::icon::{Icon, render as icon_render};
use super::message::DirectoryTreeEvent;
use super::node::TreeNode;
const INDENT_STEP: f32 = 16.0;
const INTRA_ROW_GAP: f32 = 6.0;
impl DirectoryTree {
pub fn view<'a, Message, F>(&'a self, on_event: F) -> Element<'a, Message>
where
Message: Clone + 'a,
F: Fn(DirectoryTreeEvent) -> Message + Copy + 'a,
{
let mut rows: Vec<Element<'a, Message>> = Vec::new();
render_node(&self.root, 0, on_event, &mut rows);
let list = column(rows).spacing(2).padding(4).width(Length::Fill);
scrollable(list)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
fn render_node<'a, Message, F>(
node: &'a TreeNode,
depth: u32,
on_event: F,
out: &mut Vec<Element<'a, Message>>,
) where
Message: Clone + 'a,
F: Fn(DirectoryTreeEvent) -> Message + Copy + 'a,
{
out.push(render_row(node, depth, on_event));
if node.is_dir && node.is_expanded && node.is_loaded {
for child in &node.children {
render_node(child, depth + 1, on_event, out);
}
}
}
fn render_row<'a, Message, F>(node: &'a TreeNode, depth: u32, on_event: F) -> Element<'a, Message>
where
Message: Clone + 'a,
F: Fn(DirectoryTreeEvent) -> Message + Copy + 'a,
{
let label_str: String = match node.path.file_name() {
Some(n) => n.to_string_lossy().into_owned(),
None => node.path.display().to_string(),
};
let type_icon: Element<'a, Message> = if node.error.is_some() {
icon_render::<Message>(Icon::Error)
} else if node.is_dir {
if node.is_expanded {
icon_render::<Message>(Icon::FolderOpen)
} else {
icon_render::<Message>(Icon::FolderClosed)
}
} else {
icon_render::<Message>(Icon::File)
};
let label_widget = {
let t = text(label_str).size(14);
if node.error.is_some() {
t.color(iced::Color::from_rgb(0.55, 0.55, 0.55))
} else {
t
}
};
let caret: Element<'a, Message> = if node.is_dir {
let caret_icon = if node.is_expanded {
Icon::CaretDown
} else {
Icon::CaretRight
};
let path = node.path.clone();
button(icon_render::<Message>(caret_icon))
.padding(2)
.style(button::text)
.on_press(on_event(DirectoryTreeEvent::Toggled(path)))
.into()
} else {
Space::new()
.width(Length::Fixed(20.0))
.height(Length::Fixed(20.0))
.into()
};
let selection_body = row![
type_icon,
Space::new().width(Length::Fixed(4.0)),
label_widget,
]
.spacing(INTRA_ROW_GAP)
.align_y(Alignment::Center);
let select_button = {
let path = node.path.clone();
let is_dir = node.is_dir;
button(selection_body)
.width(Length::Fill)
.padding(2)
.style(if node.is_selected {
button::primary
} else {
button::text
})
.on_press(on_event(DirectoryTreeEvent::Selected(path, is_dir)))
};
let indent_px = INDENT_STEP * depth as f32;
let indent = Space::new().width(Length::Fixed(indent_px));
container(
row![indent, caret, select_button]
.spacing(INTRA_ROW_GAP)
.align_y(Alignment::Center),
)
.width(Length::Fill)
.into()
}
#[allow(dead_code)]
fn display_path(path: &Path) -> String {
path.display().to_string()
}