Back to API Reference
Class

UniTextSelectable

sealed
Inherits:MonoBehaviour
Implements:IPointerMoveHandlerIPointerExitHandlerISelectHandlerIDeselectHandlerIInitializePotentialDragHandlerIBeginDragHandlerIDragHandlerIEndDragHandler

Selection capability for UniTextBase: caret + range state with explicit affinity, gesture interpretation (multi-click promotion, drag-to-select, word-by-word drag, context-menu coordination), and highlight rendering. Sits on the same GameObject as UniTextBase; requires an existing UniText or UniTextWorldRequireComponent validates the dependency but cannot auto-add the abstract base, so add the text component first.

Remarks

First-class component, not a modifier. The misuse pattern (selection-as-Style, empty OnApply) is gone — selection is a component-level capability with its own lifecycle, distinct from text-range markup. Add this component for read-only selectable text; combine with UniTextEditable for editable input. See Component Composition Model in the roadmap (D-008, D-011). This component owns the uGUI drag surface for text. Drag policy: mouse / pen drags select; touch drags forward to the enclosing scroll container (the iOS / Android convention — selection on touch starts from long-press or double-tap, never from a plain drag) unless a selection gesture armed word-drag mode or the editing layer claimed touch drags for a focused field. Handles, magnifier, and context menu are owner-aware serialized entities. Their implementations choose the presentation mechanism; the component never instantiates UI itself. Multi-click thresholds default to desktop conventions (MultiClickMaxInterval, MultiClickMaxDistance). Single = caret, double = word, triple = paragraph. Touch-side multi-tap thresholds (different from desktop) are fed in via DispatchTap / DispatchDoubleTap / DispatchTripleTap by the gesture recogniser, which applies its own timing.
public static float MultiClickMaxInterval{ get; set }

Maximum interval between consecutive primary clicks for multi-click detection (seconds). Defaults to 0.5 s — the Windows (GetDoubleClickTime) and macOS default. Assign to match a user-configured OS value.

public static float MultiClickMaxDistance{ get; set }

Maximum distance between consecutive primary clicks for multi-click detection, in density-independent pixels (Android dp, 160 dpi baseline) — the comparison scales by the display density at use so the physical slop stays constant on high-density displays. Defaults to 8.

public UniTextBase TextComponent{ get }

The display component this selection layer operates over. Same GameObject; lazily resolved via GetComponent<T> on first access so layout / preferred-height queries from the Unity layout system work identically in edit mode and play mode (the runtime path is the single path — no edit-time branch).

public TextSelection Selection{ get }

Current selection state (anchor / focus / affinity). Immutable.

public bool IsWordDragMode{ get }

True while word-by-word drag is active (started by a double-click on text). Drag extension snaps to whole-word boundaries instead of individual codepoints. Read by the touch gesture pipeline to decide whether to start a fresh caret-drag or continue extending the word selection.

public OrderedValueEvent<SelectionChangingArgs> SelectionChanging{ get }

Occurs when Selection is about to change through any of the public mutators (SetCaret / SetSelection / ExtendSelection / Select* / Click+drag dispatchers). Subscribers may set Cancel to abort, or assign Proposed to clamp into a permitted range (P2.A atomic-token boundary, integrator-defined read-only ranges). Side-effect movements driven by text mutation (insert / delete / IME commit / undo) bypass this event by design — they go through an internal non-vetoable path. See SelectionChangingArgs. Callbacks run by ascending order and equal orders retain subscription order.

public bool SetCaret()

Collapses the selection to a single caret at codepointIndex with the given affinity. Out-of-range indices are clamped to [0, codepointCount].

public bool SetSelection()

Sets both endpoints simultaneously. Out-of-range indices are clamped to [0, codepointCount].

public bool ExtendSelection()

Moves the focus while keeping the anchor fixed. Used for shift-click and drag extension.

public bool DragSelectionHandle()

Moves one selection endpoint to codepointIndex with the touch selection-handle contract (iOS / Android): the selection never collapses — when the dragged endpoint lands on the fixed one it is clamped one grapheme cluster away — and dragging past the fixed endpoint is allowed, inverting the anchor / focus orientation (the handles swap roles). draggingAnchor selects which endpoint moves; the other stays fixed.

public bool SelectWord()

Selects the word containing codepointIndex.

public bool SelectLine()

Selects the visual (soft-wrapped) line containing codepointIndex. Gesture pipelines use SelectParagraph for triple-click / triple-tap; this stays for consumers that genuinely want the visual row.

public bool SelectParagraph()

Selects the paragraph (between hard line breaks) containing codepointIndex.

public bool SelectAll()

Selects the entire text.

public bool ClearSelection()

Collapses the selection at the current focus.

public string GetSelectedText()

Returns the selected text as a new string. Empty string when collapsed. Reads the editing layer's document when present (authoritative during IME composition and password masking), else the rendered codepoint buffer.

public void ResetGestureState()

Resets all transient gesture state. Call on focus loss so a stale click count or drag mode does not bleed into the next focus session.

public void HandlePrimaryClick()

Programmatic primary-click driver — runs the same press gesture the pointer pipeline uses (HandlePressGesture): single places the caret, double selects the word, triple the paragraph; Shift extends. Affinity defaults downstream — the bounding-box TextHitResult carries none.

public void DispatchTap()

Drives selection from a touch-side single tap (caret placement). Bypasses the desktop multi-click counter so the touch gesture recogniser can apply its own thresholds.

public void DispatchDoubleTap()

Drives selection from a touch-side double tap (word selection) and arms IsWordDragMode.

public void DispatchTripleTap()

Drives selection from a touch-side triple tap (paragraph / line).

public void BeginDrag()

Begins a drag-to-select operation at codepointIndex. When extendOnly is, the existing selection's anchor is preserved (used by long-press → drag flow).

public void UpdateDrag()

Updates a drag-in-progress to codepointIndex. In word-drag mode, extends by whole-word boundaries; otherwise extends by single codepoints.

public void EndDrag()

Ends an active drag-to-select operation.

public void RequestContextMenu()

Shows the assigned ContextMenu (if any) with this component as the presenter, then raises ContextMenuRequested.

public void DismissContextMenu()

Hides the assigned ContextMenu (if any), then raises ContextMenuDismissRequested.

public void RefreshHighlight()

Repaints the highlight rects backing the selection. Called automatically after each selection change and after the host commits a relayout; expose for consumers that reposition the text out-of-band (manual scroll offsets) without changing Selection.

public Action<SelectionChangedArgs> SelectionChanged

Post-change notification with previous and current state plus a hierarchical UserEvent string. See SelectionChangedArgs for category strings.

public Action<TextPointerEvent> SelectionDragStarted

Occurs when a pointer drag has been routed to selection (mouse / pen always; touch only in an armed selection gesture — see the class drag policy). The event is anchored at the press position, not the position where the drag threshold was crossed. The instance is reused across emissions and Hit is not computed — hit-test from ScreenPosition.

public Action<TextPointerEvent> SelectionDragUpdated
public Action<TextPointerEvent> SelectionDragEnded
public Action<Vector2> ContextMenuRequested

Occurs when the user has requested a context menu (right-click, long-press) at a screen position — after the assigned ContextMenu (if any) is shown. Subscribe to present a custom menu.

public Action ContextMenuDismissRequested

Occurs when an open context menu should hide (drag, scroll, edit, focus loss).