use ratatui::{
buffer::Buffer,
layout::Rect,
style::{Color, Style},
widgets::StatefulWidget,
};
use crate::popup::{Popup, PopupSize};
use crate::popups::AutoSized;
use crate::tree::{Tree, TreeNode, TreeState};
use crate::{BadgeStack, BorderType, DotPattern};
#[derive(Clone)]
pub struct TreePopup<'a> {
popup: Popup<'a>,
tree: Tree<'a>,
}
impl<'a> TreePopup<'a> {
pub fn new(nodes: Vec<TreeNode<'a>>) -> Self {
Self {
popup: Popup::new(Color::White)
.padding(0)
.border_type(BorderType::Rounded),
tree: Tree::new(nodes),
}
}
pub fn title(mut self, title: &'a str) -> Self {
self.popup = self.popup.title(title);
self
}
pub fn cursor_style(mut self, style: Style) -> Self {
self.tree = self.tree.highlight_style(style);
self
}
pub fn border_color(mut self, color: Color) -> Self {
self.popup = self.popup.border_color(color);
self
}
pub fn border_type(mut self, bt: BorderType) -> Self {
self.popup = self.popup.border_type(bt);
self
}
pub fn bg_color(mut self, color: Color) -> Self {
self.popup = self.popup.bg_color(color);
self
}
pub fn width(mut self, w: PopupSize) -> Self {
self.popup = self.popup.width(w);
self
}
pub fn height(mut self, h: PopupSize) -> Self {
self.popup = self.popup.height(h);
self
}
pub fn position(mut self, x: u16, y: u16) -> Self {
self.popup = self.popup.position(x, y);
self
}
pub fn origin(mut self, x: u16, y: u16) -> Self {
self.popup = self.popup.origin(x, y);
self
}
pub fn header(mut self) -> Self {
self.popup = self.popup.header();
self
}
pub fn padding(mut self, p: u16) -> Self {
self.popup = self.popup.padding(p);
self
}
pub fn indent(mut self, n: u16) -> Self {
self.tree = self.tree.indent(n);
self
}
pub fn expand_icon(mut self, icon: &'a str) -> Self {
self.tree = self.tree.expand_icon(icon);
self
}
pub fn collapse_icon(mut self, icon: &'a str) -> Self {
self.tree = self.tree.collapse_icon(icon);
self
}
pub fn leaf_icon(mut self, icon: &'a str) -> Self {
self.tree = self.tree.leaf_icon(icon);
self
}
pub fn highlight_style(mut self, style: Style) -> Self {
self.tree = self.tree.highlight_style(style);
self
}
pub fn highlight_symbol(mut self, symbol: &'a str) -> Self {
self.tree = self.tree.highlight_symbol(symbol);
self
}
pub fn borders(mut self, borders: ratatui::widgets::Borders) -> Self {
self.tree = self.tree.borders(borders);
self
}
pub fn border_style(mut self, style: Style) -> Self {
self.tree = self.tree.border_style(style);
self
}
pub fn no_background(mut self) -> Self {
self.popup = self.popup.no_background();
self
}
pub fn background_dots(mut self, color: Color, symbol: &str, density: u16) -> Self {
self.popup = self.popup.background_dots(color, symbol, density);
self
}
pub fn background_pattern(mut self, pattern: DotPattern) -> Self {
self.popup = self.popup.background_pattern(pattern);
self
}
pub fn background_spacing(mut self, density_x: u16, density_y: u16) -> Self {
self.popup = self.popup.background_spacing(density_x, density_y);
self
}
pub fn badges(mut self, badges: BadgeStack<'a>) -> Self {
self.popup = self.popup.badges(badges);
self
}
}
impl AutoSized for TreePopup<'_> {
type State = TreeState;
fn auto_height(&self, state: &Self::State, _area: Rect) -> u16 {
let header_height: u16 = if self.popup.has_header() { 2 } else { 0 };
let tree_height = self.tree.visible_count(&state.expanded) as u16 + 2 + header_height;
tree_height.max(5)
}
}
impl<'a> TreePopup<'a> {
pub fn item_at(&self, state: &TreeState, popup_rect: Rect, row: u16) -> Option<usize> {
let content_y = popup_rect.y + self.popup.content_offset();
if row < content_y {
return None;
}
let list_row = (row - content_y) as usize;
let idx = list_row + state.scroll_state.list_state.offset();
let total = self.tree.visible_count(&state.expanded);
if idx < total {
Some(idx)
} else {
None
}
}
pub fn resolve_rect(&self, area: Rect, state: &TreeState) -> Rect {
if self.popup.get_height() == PopupSize::Auto {
self.popup
.clone()
.height(PopupSize::Fixed(self.auto_height(state, area)))
.resolve_rect(area)
} else {
self.popup.resolve_rect(area)
}
}
}
impl StatefulWidget for TreePopup<'_> {
type State = TreeState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let auto_h = if self.popup.get_height() == PopupSize::Auto {
Some(self.auto_height(state, area))
} else {
None
};
let mut popup = self.popup;
if let Some(h) = auto_h {
popup = popup.height(PopupSize::Fixed(h));
}
let inner = popup.render_inner(area, buf);
self.tree.render(inner, buf, state);
}
}
#[cfg(test)]
mod tests;