Ratatui - widgets


Show various widgets and how to work with them.


examples/ratatui/widgets/src/main.rs
use std::io;

use crossterm::event::KeyModifiers;
use ratatui::{
    buffer::Buffer,
    crossterm::event::{self, KeyCode, KeyEventKind},
    layout::Rect,
    style::{Style, Stylize},
    symbols::border,
    text::{Line, Text},
    widgets::{Block, List, ListDirection, ListState, Paragraph, StatefulWidget, Widget},
    DefaultTerminal, Frame,
};

#[derive(Debug, Default)]
struct App {
    counter: u8,
    exit: bool,
}

impl App {
    fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
        while !self.exit {
            terminal.draw(|frame| self.draw(frame))?;

            if let event::Event::Key(key) = event::read()? {
                self.handle_key_event(key);
            }
        }
        Ok(())
    }

    fn draw(&self, frame: &mut Frame<'_>) {
        frame.render_widget(self, frame.area());
    }

    fn handle_key_event(&mut self, key: event::KeyEvent) {
        if key.kind == KeyEventKind::Press {
            match key.code {
                KeyCode::Esc => self.exit = true,
                KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => self.exit = true,
                KeyCode::Right => self.counter = self.counter.saturating_add(1),
                KeyCode::Left => self.counter = self.counter.saturating_sub(1),
                _ => {}
            }
        }
    }
}

impl Widget for &App {
    fn render(self, area: Rect, buf: &mut Buffer) {
        match self.counter {
            1 => Paragraph::new(format!("{}", self.counter))
                .white()
                .on_blue()
                .render(area, buf),
            2 => Paragraph::new(format!("{}", self.counter))
                .black()
                .on_green()
                .render(area, buf),
            3 => {
                let text = Text::from(vec![Line::from(vec![
                    "Centered Value: ".to_string().black().on_white(),
                    self.counter.to_string().red(),
                ])]);

                Paragraph::new(text)
                    .centered()
                    .on_light_blue()
                    .render(area, buf);
            },

            4 => {
                let title = Line::from(" Counter App Tutorial ".bold());
                let instructions = Line::from(vec![
                    " Decrement ".into(),
                    "<Left>".blue().bold(),
                    " Increment ".into(),
                    "<Right>".blue().bold(),
                    " Quit ".into(),
                    "<Q> ".blue().bold(),
                ]);
                let block = Block::bordered()
                    .title(title.centered())
                    .title_bottom(instructions.centered())
                    .border_set(border::THICK);

                let counter_text = Text::from(vec![Line::from(vec![
                    "Value: ".into(),
                    self.counter.to_string().yellow(),
                ])]);

                Paragraph::new(counter_text)
                    .centered()
                    .block(block)
                    .render(area, buf);
            },

            5 => {
                let items = ["Item 1", "Item 2", "Item 3"];
                let list = List::new(items)
                    .block(Block::bordered().title("List"))
                    .style(Style::new().black())
                    .highlight_style(Style::new().italic())
                    .highlight_symbol(">>")
                    .repeat_highlight_symbol(true)
                    .direction(ListDirection::BottomToTop);
                Widget::render(list, area, buf);
            },

            0 => {
                let mut state = ListState::default();
let items = ["Item 1", "Item 2", "Item 3"];
let list = List::new(items)
    .block(Block::bordered().title("List"))
    .highlight_style(Style::new().reversed())
    .highlight_symbol(">>")
    .repeat_highlight_symbol(true);

    StatefulWidget::render(list, area, buf, &mut state)
            }

            _ => Paragraph::new(format!("Not implemented: {} Press the left and right arrows to cycle throught the examples.", self.counter))
                .white()
                .on_blue()
                .render(area, buf),
        }
    }
}

fn main() -> io::Result<()> {
    let mut terminal = ratatui::init();
    terminal.clear()?;
    let app_result = App::default().run(&mut terminal);
    ratatui::restore();
    app_result
}