ratkit 0.2.16

A comprehensive collection of reusable TUI components for ratatui including resizable splits, tree views, markdown rendering, toast notifications, dialogs, and terminal embedding
Documentation
use std::io;

use crossterm::event::{MouseButton, MouseEventKind};
use ratatui::{
    layout::Rect,
    text::{Line, Span},
    widgets::{Block, Borders, Paragraph},
    Frame,
};
use ratkit::primitives::button::Button;
use ratkit::{
    run_with_diagnostics, CoordinatorAction, CoordinatorApp, CoordinatorEvent, RunnerConfig,
};

struct ButtonDemo {
    button: Button,
    clicks: u32,
}

impl ButtonDemo {
    fn new() -> Self {
        Self {
            button: Button::new("Action"),
            clicks: 0,
        }
    }
}

impl CoordinatorApp for ButtonDemo {
    fn on_event(&mut self, event: CoordinatorEvent) -> ratkit::LayoutResult<CoordinatorAction> {
        match event {
            CoordinatorEvent::Keyboard(keyboard) => {
                if keyboard.is_key_down() && keyboard.is_char('q') {
                    Ok(CoordinatorAction::Quit)
                } else {
                    Ok(CoordinatorAction::Redraw)
                }
            }
            CoordinatorEvent::Mouse(mouse) => {
                match mouse.kind {
                    MouseEventKind::Moved => {
                        self.button.update_hover(mouse.column, mouse.row);
                    }
                    MouseEventKind::Down(MouseButton::Left) => {
                        self.button.update_hover(mouse.column, mouse.row);
                        if self.button.is_clicked(mouse.column, mouse.row) {
                            self.clicks += 1;
                        }
                    }
                    _ => {}
                }
                Ok(CoordinatorAction::Redraw)
            }
            _ => Ok(CoordinatorAction::Redraw),
        }
    }

    fn on_draw(&mut self, frame: &mut Frame) {
        let area = frame.area();
        let block = Block::default().borders(Borders::ALL).title(" Button ");
        let inner = block.inner(area);
        frame.render_widget(block, area);

        let button_area = button_demo_area(inner, &self.button);
        self.button.set_area(button_area);
        let button_text = format!(" [{}] ", self.button.text());
        let button_style = if self.button.hovered() {
            self.button.hover()
        } else {
            self.button.normal()
        };
        let button = Paragraph::new(Line::from(Span::styled(button_text, button_style)));
        frame.render_widget(button, button_area);

        let body_area = Rect {
            x: inner.x,
            y: inner.y + 2,
            width: inner.width,
            height: inner.height.saturating_sub(2),
        };
        let body = Paragraph::new(Line::from(format!(
            "Click the button (mouse) or press q to quit. Clicks: {}",
            self.clicks
        )));
        frame.render_widget(body, body_area);
    }
}

fn button_demo_area(inner: Rect, button: &Button) -> Rect {
    let button_width = format!(" [{}] ", button.text()).len() as u16;
    let x = inner.x + 2;
    let y = inner.y + 1;

    Rect {
        x,
        y,
        width: button_width,
        height: 1,
    }
}

fn main() -> io::Result<()> {
    let app = ButtonDemo::new();
    run_with_diagnostics(app, RunnerConfig::default())
}