tui-pages 0.7.2

Core for TUI apps with multiple pages
Documentation
# Getting Started

A whole app, top to bottom. It's two pages (Home and About) with two buttons —
Tab moves between them, Enter activates, Ctrl+C quits. This is the `minimal`
example; run it with `cd examples/minimal && cargo run`.

It's three files: `app.rs` is everything that talks to the library, `ui.rs` just
draws, `main.rs` is the loop. Start with `app.rs`.

## Your types

A `View` is a page. An `Action` is something that can happen. You define both.

```rust
use tui_pages::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum View {
    Home,
    About
}

#[derive(Debug, Clone, Copy)]
pub enum Action {
    FocusNext,
    FocusPrev,
    Select,
    Quit
}

pub type App = TuiApp<View, Action, (), Handler>;   // () = no state in this app
```

## The handler

An action comes in, you say what should happen. You don't move focus or change
pages yourself — you return a `TuiEffect` and the runtime does it.

```rust
pub struct Handler;

impl TuiActionHandler<View, Action, ()> for Handler {
    type Error = std::convert::Infallible;

    fn handle_action(&mut self, action: Action, ctx: ActionContext<View>, _state: &mut ())
        -> Result<ActionOutcome<View>, Self::Error>
    {
        Ok(match action {
            Action::FocusNext => ActionOutcome::effect(TuiEffect::Focus(FocusIntent::Next)),
            Action::FocusPrev => ActionOutcome::effect(TuiEffect::Focus(FocusIntent::Prev)),
            Action::Quit      => ActionOutcome::effect(TuiEffect::Quit),

            // Enter means different things depending on where you are.
            // `ctx` tells you the page and what's focused.
            Action::Select => match (ctx.current_view, ctx.focus) {
                (View::Home,  Some(FocusTarget::Button(0))) => ActionOutcome::effect(TuiEffect::Navigate(View::About)),
                (View::About, Some(FocusTarget::Button(0))) => ActionOutcome::effect(TuiEffect::Navigate(View::Home)),
                (_,           Some(FocusTarget::Button(1))) => ActionOutcome::effect(TuiEffect::Quit),
                _ => ActionOutcome::none(),
            },
        })
    }
}
```

That `match action { … }` is your whole app's logic. Every arm returns an effect.

## The page spec

For each view, say what's on it: the focusable things (two buttons) and which
key modes are active.

```rust
fn page_spec(_view: &View, _state: &(), _focus: Option<&FocusTarget>) -> PageSpec {
    PageSpec::new()
        .focus(PageFocusBuilder::new().button(0).button(1))
        .modes(vec![modes::GENERAL, modes::GLOBAL])
}
```

## Build it

Bind keys to actions and assemble the runtime. `GENERAL` is the normal mode;
`GLOBAL` is always on, so Ctrl+C quits anywhere.

```rust
pub fn build() -> App {
    let mut app = TuiPages::builder(View::Home)
        .page_fn(page_spec)
        .handler(Handler)
        .bind(modes::GENERAL, "tab",       Action::FocusNext)
        .bind(modes::GENERAL, "shift+tab", Action::FocusPrev)
        .bind(modes::GENERAL, "enter",     Action::Select)
        .bind(modes::GLOBAL,  "ctrl+c",    Action::Quit)
        .build();
    app.refresh_page(&());   // load the first page's focus before drawing
    app
}
```

That's all of `app.rs`. Now the loop in `main.rs`.

## The loop

Draw, read a key, hand it to `handle_key`. When an effect asks to quit, stop.

```rust
fn main() -> anyhow::Result<()> {
    let _guard = tui_pages::terminal::enter()?;   // raw mode; restores on drop/panic
    let mut terminal = Terminal::new(CrosstermBackend::new(std::io::stderr()))?;

    let mut tui = app::build();
    let mut state = ();

    loop {
        terminal.draw(|frame| ui::render(frame, *tui.current_view(), tui.focus.current()))?;

        if let crossterm::event::Event::Key(key) = crossterm::event::read()? {
            if tui.handle_key(key, &mut state)?.quit_requested {
                break;
            }
        }
    }
    Ok(())
}
```

`ui::render` gets the current view and the focused element and draws them — it
reads state, never changes it. (It's plain ratatui; look at `examples/minimal/src/ui.rs`.)

## That's the loop

Read a key → `handle_key` turns it into an action → your handler returns an
effect → the runtime applies it → draw again.

The rest of the book is just more effects you can return (focus, navigation,
panes) and more you can put in a `PageSpec`.