use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use ratatui::{ backend::{Backend, CrosstermBackend}, style::{Color, Modifier, Style}, widgets::Block, Terminal, }; use std::error::Error; use std::io; use tui_tree_widget::{Tree, TreeItem, TreeState}; struct App<'a> { state: TreeState<&'static str>, items: Vec>, } impl<'a> App<'a> { fn new() -> Self { Self { state: TreeState::default(), items: vec![ TreeItem::new_leaf("a", "Alfa"), TreeItem::new( "b", "Bravo", vec![ TreeItem::new_leaf("c", "Charlie"), TreeItem::new( "d", "Delta", vec![ TreeItem::new_leaf("e", "Echo"), TreeItem::new_leaf("f", "Foxtrot"), ], ) .expect("all item identifiers are unique"), TreeItem::new_leaf("g", "Golf"), ], ) .expect("all item identifiers are unique"), TreeItem::new_leaf("h", "Hotel"), ], } } } fn main() -> Result<(), Box> { // Terminal initialization enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; // App let app = App::new(); let res = run_app(&mut terminal, app); // restore terminal disable_raw_mode()?; execute!( terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture )?; terminal.show_cursor()?; if let Err(err) = res { println!("{err:?}"); } Ok(()) } fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<()> { loop { terminal.draw(|f| { let area = f.size(); let items = Tree::new(app.items.clone()) .expect("all item identifiers are unique") .block(Block::bordered().title(format!("Tree Widget {:?}", app.state))) .highlight_style( Style::new() .fg(Color::Black) .bg(Color::LightGreen) .add_modifier(Modifier::BOLD), ) .highlight_symbol(">> "); f.render_stateful_widget(items, area, &mut app.state); })?; if let Event::Key(key) = event::read()? { match key.code { KeyCode::Char('q') => return Ok(()), KeyCode::Char('\n' | ' ') => app.state.toggle_selected(), KeyCode::Left => app.state.key_left(), KeyCode::Right => app.state.key_right(), KeyCode::Down => app.state.key_down(&app.items), KeyCode::Up => app.state.key_up(&app.items), KeyCode::Home => { app.state.select_first(&app.items); } KeyCode::End => { app.state.select_last(&app.items); } _ => {} } } } }