Docs/Interface

Actions and text editing

Scoped actions

ActionScope stores bindings created by Context::on_action. Resolution starts at the focused node and walks ancestors until a modal boundary. A disabled local binding blocks the same action in a parent.

The runtime action test shows a current Render implementation: it creates an action binding with Context::on_action, attaches ActionId to an Element, and installs the binding through ActionScope::new. Keep the action's enabled state and its visible control in sync.

Use the same ActionState for the binding and its controls. Invocation always rechecks the retained scope. prevent_default() cancels a default action; stopping propagation alone does not.

ActionId::{COPY,CUT,PASTE,SELECT_ALL,UNDO,REDO} use the editor pipeline and can be overridden in a scope. Shortcuts are single key combinations and are suppressed during IME composition.

Menus and command palettes carry an ActionInvocation plus current state. Capture an invocation before moving focus when an overlay should act on an editor. The application handles returned focus, close, toggle, and invoke responses.

Controlled editors

TextInput and TextArea are composed UI elements. Their retained state is keyed by stable NodeId and stores UTF-8 text, caret, selection, IME preedit, and scroll position.

Rebuilding with the same authored value preserves editing state. A different external value replaces the buffer, cancels preedit, clears history, and clamps positions to grapheme boundaries. Use a different element key when switching documents.

Input::on_edit and TextArea::on_edit deliver a TextEdit: one half-open UTF-8 byte range in the previous controlled value plus its replacement. Apply it with TextEdit::apply_to to mutate only that range. This is the preferred path for code editors because ordinary typing no longer clones the complete document for history or callback delivery. on_input(String) remains compatible and the engine materializes that complete value only when an input listener exists.

History stores the removed and inserted fragments for normal edits. Undo or redo may consolidate a grouped transaction into one delta; only that uncommon path snapshots the current value to calculate the combined replacement.

Cosmic Text supplies shaping, bidi-aware caret stops, word boundaries, selection rectangles, and wrapping. Editing never splits a grapheme. Boundary affinity keeps the correct visual caret when one byte position has two positions at a left-to-right/right-to-left transition.

Common behavior:

  • double-click selects a word; triple-click selects a paragraph;
  • Shift-click extends the existing anchor;
  • arrows move visually and preserve bidi affinity;
  • Home/End use the visual line; Ctrl/Cmd+Home/End use the document;
  • word navigation and deletion follow platform modifiers;
  • TextInput submits on Enter;
  • TextArea inserts a newline on Enter and submits on Ctrl/Cmd+Enter.

Caret and selection changes reuse shaped input buffers. Taffy runs only when layout inputs change. TextArea clips text, selection, and caret to its viewport and shares one offset between caret reveal, wheel input, and scrollbar dragging.

Non-wrapping editors cache their source-line index and widest-line estimate with the shaped buffer. Large documents retain a visible text window plus overscan; pixel scrolls translate that prepared window and reshape only after the viewport crosses its edge. Editing or replacing the controlled value invalidates the cached source metadata normally.

Winit keyboard and IME events are normalized by argui-platform. argui-runtime routes them to the focused editor and updates the native IME candidate position. Native clipboard uses arboard; Web uses the asynchronous Clipboard API through the same runtime result path.

Filters

TextInputFilter applies before a controlled value changes:

Filter Accepted draft
Any arbitrary text
Decimal decimal intermediate states such as empty, sign, or period
Arithmetic arithmetic character set; expressions are not evaluated

Application-specific ranges and meaning remain application validation.

Undo and redo

History is per retained editor. Defaults are 100 transactions, 1 MiB of changed UTF-8 text, and a 750 ms grouping interval. Configure HistoryConfig on an editor or subtree; zero transactions disables history.

Contiguous typing and same-direction deletion group. Navigation, selection, focus, paste, programmatic replacement, and IME commit end a group. Undo restores selection direction and affinity. A new accepted edit after undo drops redo. Rejected edits do not enter history.

cx.edit_text(target, value) performs one undoable replacement. can_undo, can_redo, editor event snapshots, and scoped actions expose availability.

Password fields

InputKind::Password paints one bullet per grapheme and maps display positions back to the real buffer. TextPrivacy::RevealedPassword reveals it visually without enabling Copy, Cut, or undo history.

Semantic snapshots and DevTools summaries omit the value. The Web bridge remains a real input[type=password] even while visually revealed. The owning Rust model still contains the actual value; this API does not provide encrypted or zeroized storage.

Resize handles

Resize is normal composition. Attach a PanGesture to any element and handle its typed start/change/end/cancel events in model state. The application owns constraints, reset behavior, and persistence.

Examples and tests

The Solid and React gallery's InputField pages demonstrate editing, search, disabled state, and submit behavior in Solid and React. Rust editor behavior is covered by the text-input tests and the tests under crates/argui-ui/tests/text_input/. For GUI checks, follow the private display procedure and inspect saved captures.