rat-widget 3.0.2

ratatui widgets extended edition
Documentation
use crate::mini_salsa::text_input_mock::{TextInputMock, TextInputMockState};
use crate::mini_salsa::{MiniSalsaState, mock_init, run_ui, setup_logging};
use rat_theme4::StyleName;
use rat_theme4::palette::Colors;
use rat_widget::button::ButtonStyle;
use rat_widget::checkbox::{CheckboxCheck, CheckboxStyle};
use rat_widget::choice::{ChoiceClose, ChoiceFocus, ChoiceSelect, ChoiceStyle};
use rat_widget::event::{HandleEvent, Outcome, event_flow};
use rat_widget::focus::{FocusBuilder, FocusFlag, HasFocus};
use rat_widget::popup::{Placement, PopupStyle};
use rat_widget::toolbar::{Toolbar, ToolbarKeys, ToolbarOutcome, ToolbarState, ToolbarStyle};
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Constraint, Layout, Rect};
use ratatui_core::style::Style;
use ratatui_core::widgets::StatefulWidget;
use ratatui_crossterm::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};

mod mini_salsa;

fn main() -> Result<(), anyhow::Error> {
    setup_logging()?;

    let mut state = State {
        tools: ToolbarState::default(),
        anticipate: false,
        beatificate: false,
        chosen: 0,
        text_0: Default::default(),
    };

    run_ui("button1", mock_init, event, render, &mut state)
}

struct State {
    tools: ToolbarState,

    anticipate: bool,
    beatificate: bool,
    chosen: usize,

    text_0: TextInputMockState,
}

impl HasFocus for State {
    fn build(&self, builder: &mut FocusBuilder) {
        builder.widget(&self.tools);
        builder.widget(&self.text_0);
    }

    fn focus(&self) -> FocusFlag {
        todo!()
    }

    fn area(&self) -> Rect {
        todo!()
    }
}

fn render(
    buf: &mut Buffer,
    area: Rect,
    ctx: &mut MiniSalsaState,
    state: &mut State,
) -> Result<(), anyhow::Error> {
    let l0 = Layout::vertical([
        Constraint::Length(1), //
        Constraint::Fill(1),
    ])
    .split(area);

    let style = ToolbarStyle {
        style: ctx.theme.style_style(Style::STATUS_BASE),
        key_style: Some(ctx.theme.style_style(Style::KEY_BINDING)),
        button: Some(ButtonStyle {
            style: ctx.theme.style_style(Style::BUTTON_BASE),
            armed: Some(ctx.theme.style_style(Style::SELECT)),
            hover: Some(ctx.theme.style_style(Style::HOVER)),
            ..Default::default()
        }),
        checkbox: Some(CheckboxStyle {
            style: ctx.theme.style_style(Style::BUTTON_BASE),
            behave_check: Some(CheckboxCheck::SingleClick),
            ..Default::default()
        }),
        choice: Some(ChoiceStyle {
            style: ctx.theme.style_style(Style::BUTTON_BASE),
            button: Some(ctx.theme.style_style(Style::BUTTON_BASE)),
            select: Some(ctx.theme.style_style(Style::SELECT)),
            focus: Some(ctx.theme.style_style(Style::BUTTON_BASE)),
            popup: PopupStyle {
                placement: Some(Placement::BelowOrAbove),
                ..Default::default()
            },
            popup_style: Some(ctx.theme.style_style(Style::POPUP_BASE)),
            popup_border: Some(ctx.theme.style_style(Style::POPUP_BORDER_FG)),
            behave_focus: Some(ChoiceFocus::OpenOnFocusGained),
            behave_select: Some(ChoiceSelect::MouseClick),
            behave_close: Some(ChoiceClose::SingleClick),
            ..Default::default()
        }),
        collapsed: Some(ChoiceStyle {
            style: ctx.theme.p.style(Colors::Orange, 0),
            button: Some(ctx.theme.style_style(Style::BUTTON_BASE)),
            select: Some(ctx.theme.style_style(Style::SELECT)),
            focus: Some(ctx.theme.style_style(Style::BUTTON_BASE)),
            popup: PopupStyle {
                placement: Some(Placement::BelowOrAbove),
                ..Default::default()
            },
            popup_style: Some(ctx.theme.style_style(Style::POPUP_BASE)),
            popup_border: Some(ctx.theme.style_style(Style::POPUP_BORDER_FG)),
            behave_focus: Some(ChoiceFocus::OpenOnFocusGained),
            behave_select: Some(ChoiceSelect::MouseClick),
            behave_close: Some(ChoiceClose::SingleClick),
            ..Default::default()
        }),
        ..Default::default()
    };

    let (w, p) = Toolbar::new()
        .text("ABC")
        .collapsed_buttons("...")
        .button("F1", "Function", true)
        .button("F2", "Function", true)
        .checkbox("A", "nticipate", state.anticipate)
        .button("F3", "Function", true)
        .checkbox("B", "eatificate", state.beatificate)
        .choice("F7", ["Choose 1", "Choose 2", "Choose 3"], state.chosen)
        .styles(style)
        .into_widgets(l0[0], &mut state.tools);

    w.render(l0[0], buf, &mut state.tools);

    let mut txt_area = l0[1];
    txt_area.x += 4;
    txt_area.y += 4;
    txt_area.width = 20;
    txt_area.height = 1;
    TextInputMock::default()
        .sample("...")
        .style(ctx.theme.style_style(Style::INPUT))
        .focus_style(ctx.theme.style_style(Style::INPUT_FOCUS))
        .render(txt_area, buf, &mut state.text_0);

    p.render(l0[0], buf, &mut state.tools);

    Ok(())
}

fn event(
    event: &Event,
    ctx: &mut MiniSalsaState,
    state: &mut State,
) -> Result<Outcome, anyhow::Error> {
    ctx.focus = Some(FocusBuilder::rebuild_for(state, ctx.focus.take()));
    ctx.handle_focus(event);

    match state.tools.handle(
        event,
        ToolbarKeys {
            focus: ctx.focus(),
            keys: [
                Some(KeyEvent::new(KeyCode::F(1), KeyModifiers::NONE)),
                Some(KeyEvent::new(KeyCode::F(2), KeyModifiers::NONE)),
                Some(KeyEvent::new(KeyCode::Char('A'), KeyModifiers::SHIFT)),
                Some(KeyEvent::new(KeyCode::F(3), KeyModifiers::NONE)),
                Some(KeyEvent::new(KeyCode::Char('B'), KeyModifiers::SHIFT)),
                Some(KeyEvent::new(KeyCode::F(7), KeyModifiers::NONE)),
            ],
        },
    ) {
        ToolbarOutcome::Pressed(0) => event_flow!({
            ctx.status[0] = "button 1".into();
            Outcome::Changed
        }),
        ToolbarOutcome::Pressed(1) => event_flow!({
            ctx.status[0] = "button 2".into();
            Outcome::Changed
        }),
        ToolbarOutcome::Checked(2, v) => event_flow!({
            state.anticipate = v;
            ctx.status[0] = format!("checked {} {}", 2, v);
            Outcome::Changed
        }),
        ToolbarOutcome::Pressed(3) => event_flow!({
            ctx.status[0] = "button 3".into();
            Outcome::Changed
        }),
        ToolbarOutcome::Checked(4, v) => event_flow!({
            state.beatificate = v;
            ctx.status[0] = format!("checked {} {}", 4, v);
            Outcome::Changed
        }),
        ToolbarOutcome::Selected(5, m) => event_flow!({
            ctx.status[0] = format!("selected {} {}", 5, m);
            state.chosen = m;
            Outcome::Changed
        }),
        r => event_flow!(r),
    }

    Ok(Outcome::Continue)
}