tui-pages 0.7.2

Core for TUI apps with multiple pages
Documentation
# Focus

Focus is "which element is selected right now". The runtime owns it. You do
three things:

1. **Declare** what's focusable, in each page's `PageSpec`.
2. **Move** focus by returning `TuiEffect::Focus(intent)` from your handler.
3. **Read** the current focus when rendering, to highlight it.

You never mutate a focus manager yourself. (There is one — `tui.focus` — but you
read it, you don't drive it.)

## 1. Declaring focus targets

A `FocusTarget` is an element that can hold focus. You list them with
`PageFocusBuilder`:

```rust
PageFocusBuilder::new()
    .button(0)
    .button(1)
    .section_with_items(NOTES_SECTION, notes.len())
```

The target variants:

```rust
pub enum FocusTarget<O = ()> {
    Button(usize),                          // a button, by index
    Section(usize),                         // a collapsible group / list header
    SectionItem { section: usize, item: usize }, // an item inside a section
    CanvasField(usize),                     // a text/edit field
    InternalCanvasField(usize),             // a field skipped by Tab order
    Overlay(O),                             // one of your overlays (O = your type)
    ModalItem(usize),                       // a button inside a modal
    Custom(String),                         // an escape hatch
}
```

`Button`/`Section`/`SectionItem`/`CanvasField` cover almost everything. `Overlay`
carries *your* overlay type — see the `full` example's command bar.

## 2. Moving focus

Return a `FocusIntent` wrapped in a focus effect. The ones you'll actually use:

```rust
// from your handler:
Action::FocusNext => ActionOutcome::effect(TuiEffect::Focus(FocusIntent::Next)),
Action::FocusPrev => ActionOutcome::effect(TuiEffect::Focus(FocusIntent::Prev)),
```

| `FocusIntent` | What it does |
|---------------|--------------|
| `Next` / `Prev` | step through targets (and through a section's items when you're inside one) |
| `Activate` | "enter" the focused target — steps into a section, presses a button |
| `LeaveSection` | step back out of a section (no-op if you're not in one) |
| `Set(target)` | jump focus to a specific target |
| `Open(target)` | open an overlay target |
| `ClearOverlay` | close whatever overlay is open |

The key thing about `Next`/`Prev`: they're smart about sections. When focus is on
a section and you `Activate`, the runtime steps *into* its items; `Next`/`Prev`
then move between items, and stepping off the end moves back out to the next
top-level target. You bind the same `FocusNext` action to both `Tab` and `j` and
it just works — your handler never inspects focus to decide what a movement key
should do.

## Sections, end to end

This is the pattern that makes the section machinery pay off. Declare a section
with its item count:

```rust
fn page_spec(view: &View, _state: &State, _focus: Option<&FocusTarget>) -> PageSpec {
    let mut focus = PageFocusBuilder::new();
    match view {
        View::Notes => focus = focus.section_with_items(NOTES_SECTION, NOTES.len()).button(0),
        _ => {}
    }
    PageSpec::new().focus(focus).modes(vec![modes::GENERAL, modes::GLOBAL])
}
```

Because the item count travels with the section (via `.focus(builder)`), the
runtime can enter and walk the list itself. Your handler only describes
intent — move, activate, leave — plus the genuinely app-specific bits:

```rust
fn select(ctx: ActionContext<View>, state: &mut State) -> ActionOutcome<View> {
    match (ctx.current_view, ctx.focus) {
        // Enter on a real item: do the app thing.
        (View::Notes, Some(FocusTarget::SectionItem { section: NOTES_SECTION, item })) => {
            state.selected_note = Some(item);
            ActionOutcome::effect(TuiEffect::RefreshPage)
        }
        // Enter on anything else (e.g. the section header) → let the runtime
        // activate it: it steps into the section using the declared item count.
        _ => ActionOutcome::effect(TuiEffect::Focus(FocusIntent::Activate)),
    }
}
```

Bind `esc` to `FocusIntent::LeaveSection` and you have full list navigation
without a single focus inspection in your movement code. (This is the `full`
example.)

## 3. Reading focus when rendering

`tui.focus.current()` returns the focused target (owned `Option<FocusTarget<O>>`),
which is exactly what your renderer needs to highlight the right thing:

```rust
pub fn render(frame: &mut Frame, view: View, focus: Option<FocusTarget>) {
    let focused = matches!(focus, Some(FocusTarget::Button(i)) if i == 0);
    // ...draw the button highlighted when `focused`.
}
```

Other read-only queries on `tui.focus`:

```rust
tui.focus.current()      // Option<FocusTarget<O>> — the focused target
tui.focus.has_overlay()  // bool — is an overlay/modal open?
tui.focus.is_focused(&t) // bool — is this exact target focused?
```

## Wrap behaviour

`FocusWrap` (set on the builder) decides what happens at the ends of a list:
`Clamp` stops, `Wrap` cycles. It applies to page focus, section items, buffers,
and panes alike. You can read the current policy with `tui.focus.focus_wrap()`.