use crate::interactive::{
path_of,
widgets::{entry_color, EntryMarkMap},
DisplayOptions, EntryDataBundle,
};
use dua::traverse::{Tree, TreeIndex};
use itertools::Itertools;
use std::{borrow::Borrow, path::Path};
use tui::{
buffer::Buffer,
layout::Rect,
style::{Color, Modifier, Style},
widgets::{Block, Borders, Text},
};
use tui_react::{fill_background_to_right, List, ListProps};
pub struct EntriesProps<'a> {
pub tree: &'a Tree,
pub root: TreeIndex,
pub display: DisplayOptions,
pub selected: Option<TreeIndex>,
pub entries: &'a [EntryDataBundle],
pub marked: Option<&'a EntryMarkMap>,
pub border_style: Style,
pub is_focussed: bool,
}
#[derive(Default)]
pub struct Entries {
pub list: List,
}
impl Entries {
pub fn render<'a>(
&mut self,
props: impl Borrow<EntriesProps<'a>>,
area: Rect,
buf: &mut Buffer,
) {
let EntriesProps {
tree,
root,
display,
entries,
selected,
marked,
border_style,
is_focussed,
} = props.borrow();
let list = &mut self.list;
let is_top = |node_idx| {
tree.neighbors_directed(node_idx, petgraph::Incoming)
.next()
.is_none()
};
let total: u64 = entries.iter().map(|b| b.data.size).sum();
let title = match path_of(tree, *root).to_string_lossy().to_string() {
ref p if p.is_empty() => Path::new(".")
.canonicalize()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| String::from(".")),
p => p,
};
let title = format!(
" {} ({} item{})",
title,
entries.len(),
match entries.len() {
1 => "",
_ => "s",
}
);
let block = Block::default()
.title(&title)
.border_style(*border_style)
.borders(Borders::ALL);
let entry_in_view = selected.map(|selected| {
entries
.iter()
.find_position(|b| b.index == selected)
.map(|(idx, _)| idx)
.unwrap_or(0)
});
let props = ListProps {
block: Some(block),
entry_in_view,
};
let lines = entries.iter().map(
|EntryDataBundle {
index: node_idx,
data: w,
is_dir,
exists,
}| {
let mut style = Style::default();
let is_selected = if let Some(idx) = selected {
*idx == *node_idx
} else {
false
};
if is_selected {
style.modifier.insert(Modifier::REVERSED);
}
if *is_focussed & is_selected {
style.modifier.insert(Modifier::BOLD);
}
let bytes = Text::Styled(
format!(
"{:>byte_column_width$}",
display.byte_format.display(w.size).to_string(), byte_column_width = display.byte_format.width()
)
.into(),
Style {
fg: Color::Green,
..style
},
);
let percentage = Text::Styled(
format!(
" |{}| ",
display.byte_vis.display(w.size as f32 / total as f32)
)
.into(),
style,
);
let name = Text::Styled(
fill_background_to_right(
format!(
"{prefix}{}",
w.name.to_string_lossy(),
prefix = if *is_dir && !is_top(*root) { "/" } else { " " }
),
area.width,
)
.into(),
{
let is_marked = marked.map(|m| m.contains_key(node_idx)).unwrap_or(false);
let fg = if !exists {
Color::Red
} else {
entry_color(style.fg, !is_dir, is_marked)
};
Style { fg, ..style }
},
);
vec![bytes, percentage, name]
},
);
list.render(props, lines, area, buf);
}
}