Skip to content

Input

Input is a controlled single-line text component. It owns no text; your app stores an InputState, and the component emits the next state after each edit. InputWidget is the paint-only layer.

Focus the demo, type into the focused input, Tab between fields, and press Alt+T to choose a bundled theme.

Interactive Component

rust
use ratcn::{Input, edit::InputState};
use ratcn::view::View;

struct AppState {
    focus: FocusState,
    theme: Theme,
    query: InputState,
    query_invalid: bool,
}

enum Msg {
    FocusChanged(FocusState),
    QueryChanged(InputState),
    Search,
}

View::new("root")
    .focus(|s: &AppState| &s.focus, Msg::FocusChanged)
    .theme(|s: &AppState| &s.theme)
    .child(
        "query",
        Input::new()
            .value(|s: &AppState| &s.query)
            .on_change(Msg::QueryChanged)
            .on_enter(Msg::Search)
            .title("Search")
            .placeholder("Type to search...")
            .invalid(|s: &AppState| s.query_invalid),
    )
    .content(|frame, _state, view| view.render_child(frame, "query", area));

State Model

InputState stores both the string and the cursor. Because the value lives in app state, other messages can clear, prefill, validate, or replace the input without asking the component for anything.

rust
let empty = InputState::default();
let prefilled = InputState::new("hello");

The edit engine uses character indexes, not byte offsets, so cursor movement, deletion, and insertion are UTF-8 safe.

Input API

  • Input::new(): Creates an unbound input. Without .value(...), it only shows its placeholder.
  • .value(|state| &state.field): Binds the displayed value to an InputState in app state.
  • .on_change(Msg::Ctor): Emits the next InputState after edits. Omit for read-only display.
  • .on_enter(msg): Emits msg on unmodified Enter.
  • .title("..."): Draws a themed border with a left-aligned title.
  • .placeholder("..."): Text shown dimmed when the value is empty.
  • .disabled(|state| ...): Renders disabled, ignores edits, and leaves Tab traversal while true.
  • .invalid(|state| ...): Applies invalid styling while staying editable and focusable.

Events

Input emits through .on_change(...) after edits and .on_enter(...) on unmodified Enter. If .value(...) or .on_change(...) is missing, edit events are ignored because the component has nowhere to read or persist state. Disabled inputs ignore events and are skipped by Tab traversal.

Plain character keys insert text. Arrow keys, Home, End, Backspace, Delete, and common Ctrl word/line shortcuts are handled by the shared edit engine. Paste is normalized to a single line: line breaks and tabs become spaces, other control characters are dropped.

Paint Widget

Use InputWidget when you already own focus, events, and editing and only want the styled renderer.

rust
use ratcn::{InputWidget, Theme};

frame.render_widget(
    InputWidget::new(value)
        .placeholder("Enter your name...")
        .cursor(cursor)
        .focused(is_focused)
        .disabled(is_disabled)
        .invalid(is_invalid)
        .themed(&theme),
    area,
);
MethodDescription
InputWidget::new(value)Creates a paint-only input line.
.placeholder(text)Placeholder shown when value is empty and unfocused.
.cursor(index)Character index of the cursor.
.focused(bool)Draws cursor and focused background.
.is_focused()Returns whether focused styling is enabled.
.disabled(bool)Draws disabled styling.
.is_disabled()Returns whether disabled styling is enabled.
.invalid(bool)Draws invalid styling.
.is_invalid()Returns whether invalid styling is enabled.
.themed(&theme)Applies theme colors.
.style(TextInputStyle)Uses an explicit text input style.
.text_color(...), .placeholder_color(...), .background_color(...), .focused_background_color(...), .cursor_color(...)Override individual colors.