Getting Started - tui-pages

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# 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.

    #![allow(unused)]
    fn main() {
    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.

    #![allow(unused)]
    fn main() {
    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.

    #![allow(unused)]
    fn main() {
    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.

    #![allow(unused)]
    fn main() {
    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.

    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`.