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;
TextInputsubmits on Enter;TextAreainserts 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.