Focus Management - 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

# 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`:

    #![allow(unused)]
    fn main() {
    PageFocusBuilder::new()
        .button(0)
        .button(1)
        .section_with_items(NOTES_SECTION, notes.len())
    }

The target variants:

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

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

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

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

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

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