rat-widget 3.2.1

ratatui widgets extended edition
Documentation
use crate::mini_salsa::{MiniSalsaState, layout_grid, mock_init, run_ui, setup_logging};
use log::debug;
use rat_event::{HandleEvent, Popup, Regular, try_flow};
use rat_focus::{Focus, FocusBuilder};
use rat_menu::event::MenuOutcome;
use rat_menu::menuline::{MenuLine, MenuLineState};
use rat_theme4::WidgetStyle;
use rat_widget::choice::{Choice, ChoiceState};
use rat_widget::event::{ChoiceOutcome, Outcome};
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Constraint, Flex, Layout, Rect};
use ratatui_core::style::Stylize;
use ratatui_core::text::{Line, Span};
use ratatui_core::widgets::StatefulWidget;
use ratatui_crossterm::crossterm::event::Event;
use ratatui_widgets::block::Block;
use ratatui_widgets::borders::BorderType;

mod mini_salsa;

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

    let mut state = State {
        c1: ChoiceState::named("c1"),
        c2: ChoiceState::named("c2"),
        c3: ChoiceState::named("c3"),
        menu: MenuLineState::named("menu"),
    };

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

struct State {
    c1: ChoiceState,
    c2: ChoiceState,
    c3: ChoiceState<Option<usize>>,
    menu: MenuLineState,
}

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

    let lg = layout_grid::<2, 4>(
        l1[0],
        Layout::horizontal([
            Constraint::Length(25), //
            Constraint::Fill(1),
            Constraint::Length(25),
        ])
        .flex(Flex::Start),
        Layout::vertical([
            Constraint::Fill(1),
            Constraint::Length(1),
            Constraint::Length(2),
            Constraint::Length(3),
        ])
        .spacing(1),
    );

    let (w, p1) = Choice::new()
        .auto_item("Carrots ๐Ÿฅ•")
        .auto_item("Potatoes ๐Ÿฅ”")
        .auto_item("Onions ๐Ÿง…")
        .auto_item("Peas")
        .auto_item("Beans")
        .auto_item(Line::from_iter([
            Span::from("T").red(),
            Span::from("omatoes ๐Ÿ…"),
        ]))
        .auto_item(Line::from_iter([
            Span::from("Aubergines "),
            Span::from("๐Ÿ†"),
        ]))
        .auto_item("Chili")
        .auto_item("ร„pfel ๐ŸŽ")
        .auto_item("...")
        .popup_boundary(l1[0])
        .styles(ctx.theme.style(WidgetStyle::CHOICE))
        .into_widgets();
    w.render(lg[1][1], buf, &mut state.c1);

    let (w, p2) = Choice::new()
        .auto_item("wine")
        .auto_item("beer")
        .auto_item("water")
        .popup_boundary(l1[0])
        .styles(ctx.theme.style(WidgetStyle::CHOICE))
        .into_widgets();
    w.render(lg[1][2], buf, &mut state.c2);

    let (w, p3) = Choice::<Option<usize>>::new()
        .item(None, "red")
        .item(Some(0), "blue")
        .item(Some(1), "green")
        .block(Block::bordered().border_type(BorderType::Rounded))
        .popup_boundary(l1[0])
        .styles(ctx.theme.style(WidgetStyle::CHOICE))
        .popup_block(Block::bordered().border_type(BorderType::Rounded))
        .into_widgets();
    w.render(lg[1][3], buf, &mut state.c3);

    p1.render(lg[1][1], buf, &mut state.c1);
    p2.render(lg[1][2], buf, &mut state.c2);
    p3.render(lg[1][3], buf, &mut state.c3);

    MenuLine::new()
        .title("a|b|c")
        .item_parsed("_Quit")
        .styles(ctx.theme.style(WidgetStyle::MENU))
        .render(l1[1], buf, &mut state.menu);

    Ok(())
}

fn focus(state: &mut State) -> Focus {
    let mut fb = FocusBuilder::new(None);
    fb.widget(&state.menu);
    fb.widget(&state.c1);
    fb.widget(&state.c2);
    fb.widget(&state.c3);
    let f = fb.build();
    f
}

fn event(
    event: &Event,
    ctx: &mut MiniSalsaState,
    state: &mut State,
) -> Result<Outcome, anyhow::Error> {
    let mut focus = focus(state);
    ctx.focus_outcome = focus.handle(event, Regular);

    // popup handling first
    try_flow!(state.c1.handle(event, Popup));
    try_flow!(state.c2.handle(event, Popup));
    try_flow!(match state.c3.handle(event, Popup) {
        ChoiceOutcome::Value => {
            debug!("c3 {:?}", state.c3.value());
            Outcome::Changed
        }
        r => r.into(),
    });

    try_flow!(match state.menu.handle(event, Regular) {
        MenuOutcome::Activated(v) => {
            match v {
                0 => {
                    ctx.quit = true;
                    Outcome::Changed
                }
                _ => Outcome::Changed,
            }
        }
        r => r.into(),
    });

    Ok(Outcome::Continue)
}