Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
lib: Add selection example
Erik Kundt committed 1 year ago
commit a7f99eb1eb91a6c5d2c66dce8e701aa305e8d787
parent c39dd153b661b421e60dd6283ca2070098da6484
1 file changed +155 -0
added examples/selection.rs
@@ -0,0 +1,155 @@
+
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<Item>,
+
    selector: TableState,
+
}
+

+
#[derive(Clone, Debug)]
+
enum Message {
+
    SelectionChanged { state: TableState },
+
    Return,
+
    Quit,
+
}
+

+
impl Update<Message> for App {
+
    type Return = usize;
+

+
    fn update(&mut self, message: Message) -> Option<tui::Exit<Self::Return>> {
+
        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<Message> for App {
+
    fn show(&self, ctx: &Context<Message>, 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(())
+
}