use crate::{
core::{
crossterm::{
self,
event::Event,
style::{Attribute, Attributes, Color, ContentStyle},
},
render::{Renderer, SharedRenderer},
Widget,
},
preset::Evaluator,
widgets::{
text::{self, Text},
tree::{self, config::Config, node::Node},
},
Signal,
};
pub mod evaluate;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Index {
Title = 0,
Tree = 1,
}
pub struct Tree {
pub renderer: Option<SharedRenderer<Index>>,
pub evaluator: Evaluator<Self>,
pub title: text::State,
pub tree: tree::State,
}
#[async_trait::async_trait]
impl crate::Prompt for Tree {
async fn initialize(&mut self) -> anyhow::Result<()> {
let size = crossterm::terminal::size()?;
self.renderer = Some(SharedRenderer::new(
Renderer::try_new_with_graphemes(
[
(Index::Title, self.title.create_graphemes(size.0, size.1)),
(Index::Tree, self.tree.create_graphemes(size.0, size.1)),
],
true,
)
.await?,
));
Ok(())
}
async fn evaluate(&mut self, event: &Event) -> anyhow::Result<Signal> {
let ret = (self.evaluator)(event, self).await;
let size = crossterm::terminal::size()?;
self.render(size.0, size.1).await?;
ret
}
type Return = Vec<String>;
fn finalize(&mut self) -> anyhow::Result<Self::Return> {
Ok(self.tree.tree.get())
}
}
impl Tree {
pub fn new(root: Node) -> Self {
Self {
renderer: None,
evaluator: |event, ctx| Box::pin(evaluate::default(event, ctx)),
title: text::State {
config: text::config::Config {
style: Some(ContentStyle {
attributes: Attributes::from(Attribute::Bold),
..Default::default()
}),
..Default::default()
},
..Default::default()
},
tree: tree::State {
tree: tree::Tree::new(root),
config: Config {
folded_symbol: String::from("▶︎ "),
unfolded_symbol: String::from("▼ "),
active_item_style: ContentStyle {
foreground_color: Some(Color::DarkCyan),
..Default::default()
},
inactive_item_style: ContentStyle::default(),
indent: 2,
lines: Default::default(),
},
},
}
}
pub fn title<T: AsRef<str>>(mut self, text: T) -> Self {
self.title.text = Text::from(text);
self
}
pub fn title_style(mut self, style: ContentStyle) -> Self {
self.title.config.style = Some(style);
self
}
pub fn folded_symbol<T: AsRef<str>>(mut self, symbol: T) -> Self {
self.tree.config.folded_symbol = symbol.as_ref().to_string();
self
}
pub fn unfolded_symbol<T: AsRef<str>>(mut self, symbol: T) -> Self {
self.tree.config.unfolded_symbol = symbol.as_ref().to_string();
self
}
pub fn active_item_style(mut self, style: ContentStyle) -> Self {
self.tree.config.active_item_style = style;
self
}
pub fn inactive_item_style(mut self, style: ContentStyle) -> Self {
self.tree.config.inactive_item_style = style;
self
}
pub fn tree_lines(mut self, lines: usize) -> Self {
self.tree.config.lines = Some(lines);
self
}
pub fn indent(mut self, indent: usize) -> Self {
self.tree.config.indent = indent;
self
}
pub fn evaluator(mut self, evaluator: Evaluator<Self>) -> Self {
self.evaluator = evaluator;
self
}
async fn render(&mut self, width: u16, height: u16) -> anyhow::Result<()> {
match self.renderer.as_ref() {
Some(renderer) => {
renderer
.update([
(Index::Title, self.title.create_graphemes(width, height)),
(Index::Tree, self.tree.create_graphemes(width, height)),
])
.render()
.await
}
None => Err(anyhow::anyhow!("Renderer not initialized")),
}
}
}