use ratatui::prelude::*;
use ratatui::widgets::Paragraph;
use crate::component::{EventContext, RenderContext};
use crate::scroll::ScrollState;
use crate::theme::Theme;
use super::{Tree, TreeState};
impl<T: Clone + 'static> Tree<T> {
pub(super) fn render_lines(
state: &TreeState<T>,
width: u16,
theme: &Theme,
ctx: &EventContext,
) -> Vec<Line<'static>> {
let flat = state.flatten();
let mut lines = Vec::with_capacity(flat.len());
let max_depth = flat.iter().map(|n| n.depth).max().unwrap_or(0);
let indents: Vec<String> = (0..=max_depth).map(|d| " ".repeat(d)).collect();
let normal_style = theme.normal_style();
let disabled_style = if ctx.disabled {
Some(theme.disabled_style())
} else {
None
};
let highlight_style = if !ctx.disabled {
Some(theme.selected_highlight_style(ctx.focused))
} else {
None
};
let mut buf = String::with_capacity(width as usize);
for (idx, node) in flat.iter().enumerate() {
let is_selected = state.selected_index == Some(idx);
let indicator = if node.has_children {
if node.is_expanded { "▼ " } else { "▶ " }
} else {
" "
};
buf.clear();
buf.push_str(&indents[node.depth]);
buf.push_str(indicator);
buf.push_str(&node.label);
let pad = (width as usize).saturating_sub(buf.len());
for _ in 0..pad {
buf.push(' ');
}
let style = if let Some(ds) = disabled_style {
ds
} else if is_selected {
highlight_style.unwrap()
} else {
normal_style
};
lines.push(Line::from(Span::styled(buf.clone(), style)));
}
lines
}
}
pub(super) fn view<T: Clone + 'static>(state: &TreeState<T>, ctx: &mut RenderContext<'_, '_>) {
let event_ctx = ctx.event_context();
let all_lines = Tree::render_lines(state, ctx.area.width, ctx.theme, &event_ctx);
let viewport_height = ctx.area.height as usize;
let mut bar_scroll = ScrollState::new(all_lines.len());
bar_scroll.set_viewport_height(viewport_height);
if let Some(idx) = state.selected_index {
bar_scroll.ensure_visible(idx);
}
let range = bar_scroll.visible_range();
let visible_lines: Vec<Line<'static>> = all_lines
.into_iter()
.skip(range.start)
.take(range.len())
.collect();
let text = Text::from(visible_lines);
let paragraph = Paragraph::new(text);
let annotation = crate::annotation::Annotation::new(crate::annotation::WidgetType::Tree)
.with_id("tree")
.with_focus(ctx.focused)
.with_disabled(ctx.disabled);
let annotated = crate::annotation::Annotate::new(paragraph, annotation);
ctx.frame.render_widget(annotated, ctx.area);
crate::scroll::render_scrollbar(&bar_scroll, ctx.frame, ctx.area, ctx.theme);
}