use std::path::Path;
use ratatui::{prelude::*, widgets::StatefulWidget};
use super::FileTreeWidgetState;
use crate::tree::FileTree;
pub struct FileTreeWidget<'a> {
tree: &'a FileTree,
cwd: Option<&'a Path>,
}
impl<'a> FileTreeWidget<'a> {
pub fn new(tree: &'a FileTree, cwd: Option<&'a Path>) -> Self {
Self { tree, cwd }
}
}
impl<'a> StatefulWidget for FileTreeWidget<'a> {
type State = FileTreeWidgetState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let nodes = self.tree.nodes();
let visible_height = area.height as usize;
let start = state.offset;
let end = (start + visible_height).min(nodes.len());
for (i, idx) in (start..end).enumerate() {
if idx >= nodes.len() {
break;
}
let node = &nodes[idx];
let y = area.y + i as u16;
if y >= area.y + area.height {
break;
}
let indent = " ".repeat(node.depth);
let icon = node.expanded_icon(true);
let is_cwd = self.cwd.is_some_and(|cwd| node.is_dir && node.path == cwd);
let line = if is_cwd {
format!("{}{}● {}", indent, icon, node.name)
} else {
format!("{}{} {}", indent, icon, node.name)
};
let style = if is_cwd {
Style::default()
.bg(Color::Rgb(80, 70, 30))
.fg(Color::Rgb(255, 220, 100))
.bold()
} else {
let color = node.display_color();
let mut s = Style::default().fg(color);
if node.is_dir {
s = s.bold();
}
s
};
if is_cwd {
for x in area.x..area.x + area.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_bg(Color::Rgb(80, 70, 30));
}
}
}
buf.set_string(area.x, y, &line, style);
let display_width = unicode_width::UnicodeWidthStr::width(line.as_str());
if display_width > area.width as usize {
if let Some(x) = area.x.checked_add(area.width.saturating_sub(1)) {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_symbol("…");
}
}
}
}
if nodes.len() > visible_height {
let scrollbar_height =
visible_height as f32 / nodes.len() as f32 * visible_height as f32;
let scrollbar_height = scrollbar_height.max(1.0) as u16;
let scrollbar_pos =
(state.offset as f32 / nodes.len() as f32 * visible_height as f32) as u16;
let scrollbar_x = area.x + area.width - 1;
for y in 0..visible_height as u16 {
let ch = if y >= scrollbar_pos && y < scrollbar_pos + scrollbar_height {
"█"
} else {
"░"
};
if let Some(cell) = buf.cell_mut((scrollbar_x, area.y + y)) {
cell.set_symbol(ch);
cell.set_fg(Color::DarkGray);
}
}
}
}
}