UniTextEditable
Editing capability for UniTextSelectable: text storage (gap buffer), undo/redo, IME composition, validators, caret rendering, input intents, touch gesture pipeline, keyboard / clipboard handling, and the public mutation API (InsertText, Cut/Copy/Paste, Undo/Redo, etc.).
Remarks
ContentSizeFitter or place it under a layout group to size its RectTransform from the text; sizing is measured through the UniTextBase component's own ILayoutElement implementation. A pointer click activates it and places the caret; Unity UI navigation reaches it through the GameObject's Selectable — the hidden UniTextFocusable the editor manages automatically, or one already present on the GameObject. Put it directly under a RectMask2D to make that parent the clipping viewport and enable internal scrolling when content overflows.public InputCaretRenderer CaretRenderer{ get; set }Renderer used for the blinking caret.
public OrderedEvent<InputEdit> InputFilter{ get }Hook: veto or rewrite any document mutation before it applies — typing, paste, IME commit, deletions, cut, transpose. Not consulted for in-progress IME composition, undo/redo replay, or programmatic Text writes. Hooks run by ascending order, each seeing the previous one's result; equal orders retain subscription order and InputEdit.Rejected is sticky. Behaviors subscribe in their OnEnable.
public OrderedEvent<KeyResolve> KeyResolver{ get }Ordered hook for contributing a key-to-action binding before the platform key map. Lower orders resolve first; after one sets an action, later hooks must preserve it.
public OrderedEvent<KeyboardRequest> KeyboardResolver{ get }Ordered hook for composing keyboard configuration each time the soft keyboard is shown. Lower orders contribute first; equal orders retain subscription order.
public LengthLimit LengthLimit{ get; set }Length cap published by the enforcing behavior (LengthLimitBehavior) for counters to display; LengthLimit.Max 0 = none. Read-only for consumers — enforcement and configuration live on the behavior, and it republishes on every edit, so an external write here would neither cap input nor survive.
public UniTextSelectable Selectable{ get }Gets the selection component required on the same GameObject.
public TextSelection Selection{ get }Current caret or selection range.
public UniTextBase TextComponent{ get }Text component whose document this editor mutates.
Text the field shows, without serialized markup and independent of the current markup presentation mode: the document with an IME composition in progress at its insertion point. While IsComposing, assigning it through SetText ends the composition and keeps its characters as committed text.
Whether a repeated outward arrow at a formatting boundary clears an explicitly retained typing context. Off unless published by TextFormattingBehavior.
public IReadOnlyList<BaseModifier> ModifiersAtCaret{ get }Modifiers currently covering the caret / selection. Reused list — copy to keep.
public PlainTextPastePolicy PlainTextPaste{ get; set }How plain-text paste is interpreted: PlainTextPastePolicy.Literal inserts it verbatim, PlainTextPastePolicy.Parse reparses it as this field's markup, PlainTextPastePolicy.Auto (default) picks by MarkupVisibility.
public TypingMarkupPolicy TypingMarkup{ get; set }Whether hand-typed markup is parsed or kept literal — see TypingMarkupPolicy. Default TypingMarkupPolicy.Parse.
public static UniTextEditable ActiveEditor{ get }Currently active editor across the application — THE "current editor" authority, for code that needs to know which field is being edited rather than to render against one. Selection UI takes its geometry from the UniTextSelectable presenting it, which exists with or without an editing layer. Set on Activate, cleared when Deactivate completes.
public MarkupVisibility MarkupVisibility{ get; set }How this field presents its markup tags — hidden and atomic (default), revealed around the caret, or shown as raw source. Replaces the former AutoConvert flag.
public List<ChromeRule> MarkupChrome{ get }Rules styling the markup tag characters wherever they are visible — every tag under MarkupVisibility.Raw, the caret's tags under MarkupVisibility.RevealActiveRange. Each rule's ChromeRule.Selector picks which markup it targets; rules of different modifier kinds compose, same-kind rules resolve by specificity. Call RefreshMarkup after mutating at runtime.
public ValidationState Validation{ get }Current content-validity state — written by validators (via AutoValidateBehavior) or app code through SetValidation, read by field decorators.
public int CopyTextTo(Span<char> destination)Copies the currently navigable editing view, without an IME composition in progress, into a caller-provided buffer. Returns the number of UTF-16 units written without allocating; use Text for serialized markup and VisibleText for the text the field shows.
public bool TextEquals(ReadOnlySpan<char> other)Compares the currently navigable editing view, without an IME composition in progress, to a known string without allocation.
public void GetBehaviors(List<T> results)Appends every attached behavior of type T (local then preset) to results.
public bool IsStyleActive(Type modifierType)Whether typing now would produce text matching exemplar's style (by modifier signature — a composite matches its ordered child types). A pending typing style wins; otherwise the spans at the caret decide.
public bool IsStyleActive(BaseModifier exemplar)Whether typing now would produce text matching exemplar's style (by modifier signature — a composite matches its ordered child types). A pending typing style wins; otherwise the spans at the caret decide.
public bool TryGetStyleParameter(Type modifierType, out string parameter)The parameter the active style matching exemplar carries at the caret / selection — the color-swatch query for toolbars. A pending typing style wins; otherwise the covering span's parameter is returned. false when the style is not active or its value is mixed.
public bool TryGetStyleParameter(BaseModifier exemplar, out string parameter)The parameter the active style matching exemplar carries at the caret / selection — the color-swatch query for toolbars. A pending typing style wins; otherwise the covering span's parameter is returned. false when the style is not active or its value is mixed.
Inserts text at the current caret position, replacing any active selection. Convenience overload that accepts a string. See InsertText.
public void InsertText(ReadOnlySpan<char> text)Inserts text at the current caret position, replacing any active selection. Applies the configured input-filter chain and records the accepted edit for undo.
Deletes the grapheme cluster after the caret, or deletes the current selection. Equivalent to the Delete key. Document-space cluster resolution — see DeletePrevious.
Copies the selected text to the system clipboard. Does nothing if there is no selection or if the field is in password mode. Walks every built-in clipboard adapter and writes the resulting items as one atomic multi-format clipboard transaction (plain text + custom UniText format + optional HTML / Markdown), so the consumer's paste picks the richest format it understands. When the selection's markup is visible to the user — Raw, or a revealed range under MarkupVisibility.RevealActiveRange — the copy carries only that visible text as plain text and no semantic channels: what is on screen is what pastes.
Pastes text from the system clipboard at the current caret position, replacing any active selection. Probes the built-in clipboard adapters by IClipboardAdapter.Priority descending — a cheap IClipboardProvider.HasFormat availability check, then one payload transfer, then deserialize; the first success wins, so only one format ever crosses the clipboard. UniText source (lossless custom format) wins over HTML / Markdown, which win over plain text. The adapter resolves the payload to visible text and destination formatting spans, then inserts both in one document transaction. A format transfers only when the field has a style that renders it; unmatched formatting degrades to plain text. Plain text inserts verbatim.
public Task PasteAsync()Async variant of Paste. Required on WebGL for programmatic paste (toolbar icon, context menu, autofill). This is the batched collect-all read: every requested format arrives in ONE underlying clipboard access via IAsyncClipboardProvider.GetItemsAsync (WebGL's single-user-activation rule allows exactly one read); must be awaited from a user-activation context on WebGL. A provider without the async seam degrades to the sync Paste probe. Runs the same pipeline, including the MediaReceived hook.
public Task PastePlainAsync()Async variant of PastePlain — the WebGL-safe paste-without-formatting command. Same activation-context contract as PasteAsync.
public void PasteFromItems(IReadOnlyList<ClipboardItem> items)Pastes pre-extracted clipboard items through the adapter pipeline, bypassing UniTextClipboard.Provider. Used by iOS UIPasteControl and drag-drop drop sites to avoid a second pasteboard read (which on iOS would surface the system permission prompt). A format transfers only when the field has a style that renders it.
Invoked by a virtualised list host (Android RecyclerView, iOS UITableView, Unity scroll-pool, custom) when this editor's GameObject is recycled into a new cell position. Re-arms input lazily on the next focus. The owning component is expected to call ISavedStateProvider.RestoreState first when state needs to be preserved across reuse.
public void OnDetachedFromCell(Action completion)Releases editing state and invokes completion when the object may be safely recycled by its virtualised-list host.
Called by a submit-defining behavior during Submitted dispatch to release focus once the dispatch completes — deferred so every subscriber observes a still-focused editor regardless of subscription order.
Re-renders after a MarkupChrome mutation.
public bool ToggleStyle(BaseModifier exemplar, string parameter = null)Toggles the component's style matching exemplar (by modifier signature — a composite matches its ordered child types) on the selection, or the pending typing style at a caret. No-op when the component has no matching style with a wrappable rule. Returns whether on.
public void SetValidation(ValidationState state)Sets the validation state and fires ValidationChanged. The editor only holds the state; what to validate and when lives in an AutoValidateBehavior or app code (set ValidationState.Pending while an async check runs, then the result).
public void HandleScroll(PointerEventData eventData)Routes a wheel event through the shared text scrolling surface.
public Vector2 GetCaretScreenPosition(int codepointIndex, bool bottom)Screen-space caret position for codepointIndex (document space) — bottom picks the caret rect's bottom or top edge. Caret geometry exists only while a field is being edited, and the sibling selectable publishes it through UniTextSelectable.TryGetCaretScreenPoint for the selection UI to place a caret handle against.
Hook: consume media — a paste, a drag-and-drop, or a picker selection — before the text-adapter pipeline. Behaviors subscribe in their OnEnable; see MediaInputBehavior.
Occurs synchronously after a committed edit and before the frame-coalesced TextChanged event.
public Action TextChangedOccurs, at most once per frame, when the text the field shows has changed: a committed edit, an attributed range change, or any change of an IME composition in progress, its start and end included. Read that text through VisibleText; CharCount, CopyTextTo and TextEquals read the committed editing view without allocation, and Text the serialized markup.
public Action<string> ValueChangedOccurs when committed document text or attributed ranges change, so not while an IME composition is in progress. Provides the full serialized source string and allocates only when subscribed.
public Action<string> DocumentChangedOccurs alongside ValueChanged for each committed change, with the originating TextChangeReason string — distinguishes user typing (input.type) from paste (input.paste), programmatic mutation (program.set), IME commit (input.type.compose), and undo/redo replay (input.restore). Everything under input.* is user-originated.
public Action<string> SubmittedOccurs when the user has submitted. Enter inserts a newline by default — a submit-resolving behavior defines what submits: SingleLineBehavior (Enter, form field) or SubmitKeyBehavior (Enter or Ctrl/Cmd+Enter, composer). Gamepad submit (EventSystem) also fires this. Carries the current text snapshot. Post-submit focus is the submit-defining behavior's policy — both carry KeepFocusOnSubmit with archetype defaults: SingleLineBehavior releases focus (form), SubmitKeyBehavior keeps it (composer); without one, focus stays.
public Action CancelledOccurs when the user has cancelled editing — Escape (desktop), system back (mobile). The editor does not deactivate automatically; add DefocusOnCancelBehavior or subscribe directly when cancellation should release focus. Android delivers the back key only where the platform routes it to the app: an app opted into OnBackInvokedCallback dispatch (Unity's Predictive Back Support), and Android 13/14 while the keyboard is showing, dismiss the keyboard without raising this.
public Action FocusedOccurs when the editor has gained input focus (activated) — by pointer, navigation, or programmatic select.
public Action<EditingEndReason> DefocusedOccurs when the editor has lost input focus (deactivated), carrying how the session concluded. Submitted and Cancelled report the user's intent while editing continues; this reports the end itself.
public Action<int,int> SelectionChangedFrame-coalesced selection-change notification. Parameters: (selectionStart, selectionEnd) in codepoint indices. Fires at most once per frame after UniTextSelectable.SelectionChanged mutations have settled.
public Action<bool> CompositionStateChangedOccurs when IME composition state changes — fires true on start, false on commit or cancel.
public Action<CaretContext> CaretContextChangedOccurs (frame-coalesced) when the set of modifiers covering the caret / selection has changed — on caret movement, selection change, or reflow — and when a pending typing style flips (re-query IsStyleActive<T>). Subscribe from an InputBehavior or see CaretContextBehavior for the inspector-wired form.
public static Action ActiveEditorChangedOccurs after ActiveEditor changes: once an editor has finished activating, and once the active editor has finished deactivating.
public Action<ValidationState> ValidationChangedOccurs when Validation has changed.
