use std::{
hash::Hash,
mem,
ops::{Deref, DerefMut},
};
use ratatui::{
crossterm::event::KeyEvent,
prelude::{StatefulWidget as _, *},
};
use ratzgo::{
core::{Widget, *},
scroll::ScrollAction,
};
use tui_tree_widget::TreeItem;
#[derive(Debug)]
pub struct Tree<'a, I, Message> {
base: tui_tree_widget::Tree<'a, I>,
state: &'a mut TreeState<I>,
activity: bool,
on_key: OnKey<'a, Message>,
}
impl<'a, I, Message> Tree<'a, I, Message> {
pub fn new(items: &'a [TreeItem<'a, I>], state: &'a mut TreeState<I>) -> Self
where
I: Clone + PartialEq + Eq + Hash,
{
Tree {
base: tui_tree_widget::Tree::new(items).expect("each item must be unique"),
state,
activity: false,
on_key: Default::default(),
}
}
pub fn decorate<F>(mut self, f: F) -> Self
where
F: FnOnce(tui_tree_widget::Tree<'a, I>) -> tui_tree_widget::Tree<'a, I>,
{
self.base = f(self.base);
self
}
}
impl<'a, I, Message> Widget<Message> for Tree<'a, I, Message>
where
I: std::fmt::Debug + Clone + PartialEq + Eq + Hash,
Message: std::fmt::Debug,
{
fn activity(&self) -> bool {
self.activity
}
fn area(&self) -> Rect {
self.state.area
}
fn set_area(&mut self, area: Rect) {
self.state.area = area;
}
fn handle_key(&mut self, key: &KeyEvent) -> Option<Message> {
self.on_key.key(key)
}
fn adapt(&mut self, buf: &mut Buffer) {
let tree = mem::replace(
&mut self.base,
tui_tree_widget::Tree::<I>::new(&[]).expect("default tree"),
);
tree.render(self.state.area, buf, &mut self.state.base);
}
}
impl<'a, I, Message> Activable for Tree<'a, I, Message> {
fn active_mut(&mut self) -> &mut bool {
&mut self.activity
}
}
impl<'a, I, Message> OnKeyBuilder<'a, Message> for Tree<'a, I, Message> {
fn on_key_mut(&mut self) -> &mut OnKey<'a, Message> {
&mut self.on_key
}
}
impl<'a, I, Message> From<Tree<'a, I, Message>> for Element<'a, Message>
where
I: std::fmt::Debug + Clone + PartialEq + Eq + Hash,
Message: std::fmt::Debug + 'a,
{
fn from(widget: Tree<'a, I, Message>) -> Self {
Self::new(widget)
}
}
#[derive(Debug, Default)]
pub struct TreeState<I> {
base: tui_tree_widget::TreeState<I>,
pub area: Rect,
}
impl<I> Deref for TreeState<I> {
type Target = tui_tree_widget::TreeState<I>;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl<I> DerefMut for TreeState<I> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl<I> TreeState<I>
where
I: Clone + PartialEq + Eq + Hash,
{
pub fn scroll_vertical(&mut self, action: ScrollAction) {
let selected_offset = match action {
ScrollAction::Fixed(n) => n,
ScrollAction::Viewport(n) => (self.area.height as f32 * n as f32 * 0.01) as i16,
};
self.base.select_relative(|v| match v {
Some(v) => v.saturating_add_signed(selected_offset as isize),
None if selected_offset >= 0 => selected_offset as usize,
_ => 0,
});
self.base.scroll_selected_into_view();
}
}