use std::time::{SystemTime, UNIX_EPOCH}; use anyhow::Result; use termion::event::Key; use ratatui::layout::{Constraint, Layout}; use ratatui::style::Stylize; use ratatui::text::Span; use ratatui::widgets::Cell; use ratatui::{Frame, Viewport}; use radicle_tui as tui; use tui::ui::im::widget::Window; use tui::ui::im::{Borders, Context}; use tui::ui::{Column, ToRow}; use tui::Channel; use tui::{ store::Update, ui::im::{widget::TableState, Show}, Exit, }; #[derive(Clone, Debug)] struct Item { id: usize, title: String, timestamp: usize, } impl ToRow<3> for Item { fn to_row(&self) -> [Cell; 3] { [ Span::raw(self.id.to_string()).magenta().dim().into(), Span::raw(self.title.clone()).into(), Span::raw(self.timestamp.to_string()) .dark_gray() .italic() .into(), ] } } #[derive(Clone, Debug)] struct App { items: Vec, selector: TableState, } #[derive(Clone, Debug)] enum Message { SelectionChanged { state: TableState }, Return, Quit, } impl Update for App { type Return = usize; fn update(&mut self, message: Message) -> Option> { match message { Message::SelectionChanged { state } => { self.selector = state; None } Message::Return => self .selector .selected() .and_then(|selected| self.items.get(selected)) .and_then(|item| { Some(Exit { value: Some(item.id), }) }), Message::Quit => Some(Exit { value: None }), } } } impl Show for App { fn show(&self, ctx: &Context, frame: &mut Frame) -> Result<()> { Window::default().show(ctx, |ui| { ui.layout( Layout::vertical([ Constraint::Length(1), Constraint::Min(1), Constraint::Length(1), ]), |ui| { let columns = [ Column::new(Span::raw("Id").bold(), Constraint::Length(4)), Column::new(Span::raw("Title").bold(), Constraint::Fill(1)), Column::new(Span::raw("Timestamp").bold(), Constraint::Fill(1)), ] .to_vec(); let mut selected = self.selector.selected(); ui.columns(frame, columns.clone(), Some(Borders::None)); ui.set_focus(Some(1)); let table = ui.table( frame, &mut selected, &self.items, columns, Some(Borders::None), ); if table.changed { ui.send_message(Message::SelectionChanged { state: TableState::new(selected), }) } ui.shortcuts(frame, &[("q", "quit")], '|'); if ui.input_global(|key| key == Key::Char('\n')) { ui.send_message(Message::Return); } }, ); if ui.input_global(|key| key == Key::Char('q')) { ui.send_message(Message::Quit); } }); Ok(()) } } #[tokio::main] pub async fn main() -> Result<()> { let app = App { items: (0..200) .map(|id| Item { id, title: format!("Title of item #{}", id), timestamp: SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Could not read system time") .as_secs() as usize, }) .collect(), selector: TableState::new(Some(0)), }; if let Some(exit) = tui::im(app, Viewport::Inline(12), Channel::default()).await? { println!("{exit}"); } else { anyhow::bail!("No selection"); } Ok(()) }