Skip to content

View

View is the focus tree and composition layer for interactive components. It does not do layout. You keep using ordinary Ratatui layouts and widgets, then call view.render_child(...) where a registered component should appear.

In the demo, a and b jump between panes, Tab and Shift+Tab move through the focused pane, Enter or Space presses buttons, and Alt+T opens the theme picker.

Root View

The root view is where focus and theme are wired to app state.

rust
use ratcn::{Button, Theme};
use ratcn::view::{FocusPolicy, FocusState, View};

struct AppState {
    focus: FocusState,
    theme: Theme,
}

enum Msg {
    FocusChanged(FocusState),
    Save,
}

let root = View::new("root")
    .focus(|s: &AppState| &s.focus, Msg::FocusChanged)
    .theme(|s: &AppState| &s.theme)
    .focus_policy(FocusPolicy::Wrap)
    .child("save", Button::new("Save").on_press(Msg::Save))
    .content(|frame, _state, view| {
        view.render_child(frame, "save", view.area());
    });

View API

  • View::new(id): Creates a focus scope with a stable id.
  • .focusable(): Makes the view itself focusable even without focusable children.
  • .child(id, component): Registers a focusable child. Declaration order is Tab order.
  • .content(|frame, state, view| ...): Renders the view body and places registered children.
  • .focus(read, on_change): Binds the root focus path to app state.
  • .theme(read): Reads the active Theme from app state.
  • .focus_policy(policy): Controls what happens when Tab reaches the scope edge.
  • .render(frame, area, state): Paints the whole tree.
  • .handle_event(event, state): Routes input to the focused leaf and returns an EventResult.
  • .focus_path(state, &[ids]): Builds a focus path to a specific nested child.

Use .focus_path(...) for programmatic focus jumps. Passing a path to a nested view descends to that view's first focusable child.

ViewCtx API

The content closure receives ViewCtx, usually named view.

  • view.area(): The rectangle this view is rendering into.
  • view.render_child(frame, id, area): Renders a registered child with its focus context.
  • view.contains_focus(): True if the focus path passes through this view. Useful for pane borders.
  • view.theme(): The active theme, useful for static Ratatui content in the closure.

Focus Policy

FocusPolicy::Escape is the default. When Tab reaches the last focusable child, the event bubbles to the parent scope. This creates whole-tree traversal across nested views.

FocusPolicy::Wrap cycles within the current scope. It is useful for roots, dialogs, and panes that should trap Tab until app-level logic moves focus away.

Events

handle_event(...) returns EventResult<M>.

ResultMeaning
EventResult::Emit(msg)The tree handled the event and produced an app message.
EventResult::ConsumedThe tree handled the event without a message.
EventResult::IgnoredNothing handled the event; app-level shortcuts may act on it.
rust
match root.handle_event(&event, &state) {
    EventResult::Emit(msg) => update(&mut state, msg),
    EventResult::Consumed => {}
    EventResult::Ignored => handle_app_shortcut(event),
}