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

API Reference

A map of the public surface, with the signatures you'll actually call. Full generated docs live on docs.rs. Signatures here are abbreviated (bounds elided) for readability.

Prelude

#![allow(unused)]
fn main() {
use tui_pages::prelude::*;
}

Pulls in the runtime types, the focus types, modes::*, parse_binding, and — importantly — the traits TuiActionHandler, FocusController, and PageProvider. With the dialog feature it also brings in DialogData, DialogResult, DialogTheme, render_dialog, and the dialog::* helpers.

Building the app

#![allow(unused)]
fn main() {
TuiPages::builder(initial_view: V) -> TuiPagesBuilder<...>

impl TuiPagesBuilder {
    // pages + handler (both required)
    fn page_fn(self, f: PageFn<V, S, O>) -> Self;     // page provider as a fn
    fn pages(self, provider: P) -> Self;              // or a custom PageProvider
    fn handler(self, h: Handler) -> Self;

    // bindings & commands
    fn bind(self, mode: impl Into<ModeId>, binding: &str, action: A) -> Self;
    fn keymap(self, mode: impl Into<ModeId>, f: impl FnOnce(&mut KeyMap<A>)) -> Self;
    fn command(self, name: impl Into<String>, aliases, action: A) -> Self;

    // tuning (all optional)
    fn fallback_view(self, view: V) -> Self;          // view to fall back to on close
    fn focus_wrap(self, wrap: FocusWrap) -> Self;     // Clamp (default) or Wrap
    fn input_timeout_ms(self, ms: u64) -> Self;       // chord-sequence timeout
    fn command_timeout_ms(self, ms: u64) -> Self;

    fn build(self) -> TuiPages<...>;
}
}

PageFn<V, S, O> is fn(&V, &S, Option<&FocusTarget<O>>) -> PageSpec<O>.

The runtime: TuiPages

Public fields (you read these to render; you don't drive them):

#![allow(unused)]
fn main() {
pub input:    InputPipeline<A>
pub commands: CommandResolver<A>      // resolve typed commands
pub focus:    FocusManager<O, M>      // focus.current(), focus.has_overlay(), ...
pub buffer:   BufferState<V>          // buffer.panes(), buffer.is_split(), ...
}

Methods you call:

#![allow(unused)]
fn main() {
fn handle_key(&mut self, key: KeyEvent, state: &mut S)
    -> Result<TuiPagesOutput<A>, TuiPagesError<E>>;
fn submit_command(&mut self, input: &str, state: &mut S)
    -> Result<TuiPagesOutput<A>, TuiPagesError<E>>;
fn refresh_page(&mut self, state: &S);   // re-read the current page spec
fn current_view(&self) -> &V;            // active buffer's view
fn apply_effect(&mut self, effect: TuiEffect<V, O, M>, state: &S) -> bool;
}
#![allow(unused)]
fn main() {
pub struct TuiPagesOutput<A> {
    pub status: TuiPagesStatus<A>,
    pub quit_requested: bool,
}

pub enum TuiPagesStatus<A> {
    ActionHandled, TextHandled,
    Waiting(Vec<InputHint<A>>), Cancelled,
    CommandIncomplete(Vec<CommandHint>), CommandUnknown, CommandEmpty,
}
}

Your handler

#![allow(unused)]
fn main() {
pub trait TuiActionHandler<V, A, S, O = (), M = ()> {
    type Error;

    fn handle_action(&mut self, action: A, ctx: ActionContext<V, O>, state: &mut S)
        -> Result<ActionOutcome<V, O, M>, Self::Error>;

    // default returns ActionOutcome::none(); override for text-input pages
    fn handle_text(&mut self, chord: KeyChord, ctx: ActionContext<V, O>, state: &mut S)
        -> Result<ActionOutcome<V, O, M>, Self::Error>;
}

pub struct ActionContext<V, O = ()> {
    pub current_view: V,
    pub focus: Option<FocusTarget<O>>,
    pub has_overlay: bool,
}
}

Effects & outcomes

#![allow(unused)]
fn main() {
pub enum TuiEffect<V, O = (), M = ()> {
    None, Focus(FocusIntent<O, M>), Navigate(V),
    NextBuffer, PreviousBuffer, CloseBuffer,
    SplitPane(PaneSplit), ClosePane, NextPane, PreviousPane,
    RefreshPage, Quit,
}

impl ActionOutcome<V, O, M> {
    fn none() -> Self;
    fn effect(e: TuiEffect<V, O, M>) -> Self;
    fn effects(iter: impl IntoIterator<Item = TuiEffect<V, O, M>>) -> Self;
}
}

Pages & focus

#![allow(unused)]
fn main() {
pub struct PageSpec<O = ()> { /* see Core Concepts */ }
impl PageSpec<O> {
    fn new() -> Self;
    fn focus(self, builder: PageFocusBuilder<O>) -> Self;   // prefer this
    fn focus_targets(self, targets: Vec<FocusTarget<O>>) -> Self;
    fn modes(self, modes: impl IntoIterator<Item = ModeId>) -> Self;
    fn accepts_text_input(self, yes: bool) -> Self;
}

impl PageFocusBuilder<O> {
    fn new() -> Self;
    fn button(self, index: usize) -> Self;
    fn buttons(self, indices: &[usize]) -> Self;
    fn section(self, id: usize) -> Self;
    fn section_with_items(self, id: usize, item_count: usize) -> Self;
    fn canvas_field(self, index: usize) -> Self;
    fn canvas_fields(self, count: usize) -> Self;
    fn internal_canvas_field(self, index: usize) -> Self;
    fn target(self, t: FocusTarget<O>) -> Self;
    fn build(self) -> Vec<FocusTarget<O>>;
}

pub enum FocusTarget<O = ()> {
    Button(usize), Section(usize), SectionItem { section: usize, item: usize },
    CanvasField(usize), InternalCanvasField(usize),
    Overlay(O), ModalItem(usize), Custom(String),
}

pub enum FocusIntent<O = (), M = ()> {
    Next, Prev, Activate, LeaveSection,
    Set(FocusTarget<O>), Open(FocusTarget<O>), Close(FocusTarget<O>),
    Toggle(FocusTarget<O>), ClearOverlay,
    EnterSection { item_count: usize },
    ExitCanvasForward, ExitCanvasBackward,
    ShowModal { data: M, count: usize }, UpdateModal { data: M, count: usize },
    RegisterPage(Vec<FocusTarget<O>>),
    RegisterPageAndEnterSection { targets: Vec<FocusTarget<O>>, section: usize, item_count: usize, item: usize },
}

pub enum FocusWrap { Clamp, Wrap }
}

FocusManager — read-only from your side:

#![allow(unused)]
fn main() {
fn current(&self) -> Option<FocusTarget<O>>;
fn is_focused(&self, target: &FocusTarget<O>) -> bool;
fn has_overlay(&self) -> bool;
fn focus_wrap(&self) -> FocusWrap;
fn clear_overlay(&mut self);    // used when closing an app-owned palette
}

Input

#![allow(unused)]
fn main() {
fn parse_binding(s: &str) -> Vec<KeyChord>;                 // lenient, drops bad tokens
fn parse_key(s: &str) -> Option<KeyChord>;
fn try_parse_binding(s: &str) -> Result<Vec<KeyChord>, ParseKeyError>;  // strict
fn try_parse_key(s: &str) -> Result<KeyChord, ParseKeyError>;

impl KeyChord {
    fn from_event(event: &KeyEvent) -> Self;
    fn display_string(&self) -> String;
    pub code: KeyCode,
    pub modifiers: KeyModifiers,
}
}

There is no FromStr/parse() on KeyChord — use the functions above.

Commands

#![allow(unused)]
fn main() {
pub enum CommandResponse<A> { Execute(A), Incomplete(Vec<CommandHint>), Unknown, Empty }
pub struct CommandHint { pub alias: String, pub action_name: String }

impl CommandResolver<A> {           // available as tui.commands
    fn process(&self, input: &str) -> CommandResponse<A>;
    fn get_feedback(&self, input: &str) -> Option<String>;
}
}
#![allow(unused)]
fn main() {
pub enum PaneSplit { Horizontal, Vertical }

impl BufferState<V> {               // available as tui.buffer (read-only use)
    fn get_active_view(&self) -> Option<&V>;
    fn is_split(&self) -> bool;
    fn split_direction(&self) -> Option<PaneSplit>;
    fn panes(&self) -> &[PaneSession<V>];
    fn active_pane_index(&self) -> usize;
}
}

You drive buffers and panes through TuiEffect, not through these mutators.

Dialog (feature dialog)

#![allow(unused)]
fn main() {
pub struct DialogData<D = ()> {
    pub title: String, pub message: String,
    pub buttons: Vec<String>, pub purpose: Option<D>, pub is_loading: bool,
}
impl DialogData<D> {
    fn new(title, message, buttons, purpose: D) -> Self;
    fn loading(title, message) -> Self;
    fn show_intent<O>(self) -> FocusIntent<O, DialogData<D>>;
}

pub enum DialogResult<D> { Selected { purpose: Option<D>, index: usize }, Dismissed }
pub enum DialogKey<D>    { Ignored, Consumed, Resolved(DialogResult<D>) }

// helpers
fn dialog::handle_key<O, D>(focus: &mut FocusManager<O, DialogData<D>>, key: KeyEvent) -> DialogKey<D>;
fn dialog::current_dialog<O, D>(focus: &FocusManager<O, DialogData<D>>) -> Option<&DialogData<D>>;
fn dialog::active_button<O, D>(focus: &FocusManager<O, DialogData<D>>) -> Option<usize>;
fn render_dialog<D>(f: &mut Frame, area: Rect, data: &DialogData<D>, active_button: usize, theme: &DialogTheme);
}

Terminal

#![allow(unused)]
fn main() {
fn tui_pages::terminal::enter() -> io::Result<TerminalGuard>;
}

Enables raw mode + the alternate screen and returns a guard that restores the terminal when it drops — including on a panic.