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
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.
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 toTextAreaStatein app state..on_change(Msg::Ctor): Emits the nextTextAreaStateafter edits. Omit for read-only display..on_ctrl_enter(msg): Emitsmsgon 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.
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,
);| Method | Description |
|---|---|
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. |