# 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)),
```
| `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()`.