Skip to content

TextArea

TextArea is a controlled multiline editor. Your app owns the text and cursor state as TextAreaState; the component handles focus-aware rendering and maps keyboard input to edits. TextAreaWidget is the paint-only renderer.

Focus the demo, type multiple lines, and use Ctrl+Enter to emit the submit message. Press Alt+T to choose a bundled theme.

Interactive Component

rust
use ratcn::{TextArea, edit::TextAreaState};
use ratcn::view::View;

struct AppState {
    focus: FocusState,
    theme: Theme,
    message: TextAreaState,
    message_invalid: bool,
}

enum Msg {
    FocusChanged(FocusState),
    MessageChanged(TextAreaState),
    Send,
}

View::new("form")
    .focus(|s: &AppState| &s.focus, Msg::FocusChanged)
    .theme(|s: &AppState| &s.theme)
    .child(
        "message",
        TextArea::new()
            .value(|s: &AppState| &s.message)
            .on_change(Msg::MessageChanged)
            .on_ctrl_enter(Msg::Send)
            .title("Message")
            .placeholder("Write notes here...")
            .invalid(|s: &AppState| s.message_invalid),
    )
    .content(|frame, _state, view| view.render_child(frame, "message", area));

State Model

TextAreaState stores the multiline string plus row/column cursor position. Rows are split on \n; columns are character indexes, not byte offsets.

rust
let empty = TextAreaState::default();
let prefilled = TextAreaState::new("first line\nsecond line");

The component emits a complete next TextAreaState from .on_change(...). Your update function persists that snapshot into app state.

TextArea API

  • TextArea::new(): Creates an unbound controlled textarea.
  • .value(|state| &state.field): Binds the displayed value to TextAreaState in app state.
  • .on_change(Msg::Ctor): Emits the next TextAreaState after edits. Omit for read-only display.
  • .on_ctrl_enter(msg): Emits msg on Ctrl+Enter. Enter alone inserts a newline.
  • .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

TextArea emits through .on_change(...) after edits and .on_ctrl_enter(...) on Ctrl+Enter. If .value(...) or .on_change(...) is missing, edit events are ignored because the component has nowhere to read or persist state. Disabled text areas ignore events and are skipped by Tab traversal.

Enter inserts a newline. Ctrl+Enter emits .on_ctrl_enter(...) if configured. Arrow keys, Home, End, PageUp, PageDown, Backspace, Delete, and common Ctrl word/line shortcuts are handled by the shared edit engine. Paste preserves line breaks and tabs while dropping other control characters.

Paint Widget

Use TextAreaWidget when you only need rendering.

rust
use ratcn::TextAreaWidget;

frame.render_widget(
    TextAreaWidget::new(value)
        .placeholder("Write notes here...")
        .cursor(row, col)
        .scroll_row(scroll_row)
        .focused(is_focused)
        .disabled(is_disabled)
        .invalid(is_invalid)
        .themed(&theme),
    area,
);
MethodDescription
TextAreaWidget::new(value)Creates a paint-only multiline editor.
.placeholder(text)Placeholder shown on the first empty row when the value is empty.
.cursor(row, col)Cursor row and character column.
.scroll_row(row)First logical row rendered at the top of the area.
.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.