Changelog
All notable changes to UniText.
Versions
to
- AddedNew
media.lightside.corepackage ("LightSide Core", v1.0.0, Unity 2021.3+, MIT / free). UniText's non-text foundation now lives in a standalone package — object pooling (ArrayPool), the worker-thread pool (WorkerPool,MainThread), math/hashing (XxHash64,HashNoise), collections (FastIntDictionary,SpanIntern), named catalogs (INamedCatalog/AssetNamedCatalog/InlineNamedCatalog), color/gradient parsing (ColorParsing,Gradient), theCatlogging system, the touch-gesture recognizer, a full custom-inspector toolkit, and the byte-preserving asset-migration framework (IMigration,IMigratedPackage,RenameManagedType/MoveFieldsToStruct, per-package ledger) — the machinery behind UniText's automatic v2→3 upgrades, open to any package on Core. AssembliesLightSide.Core/LightSide.Core.Editor, root namespaceLightSide. - AddedOpen-core boundary. The shared foundation is MIT and free; UniText's text engine stays the commercial package. The two version independently, and UniText 3.0.0 takes a hard dependency on
media.lightside.core1.0.0, resolved automatically through themedia.lightsidescoped registry — one install pulls both. - AddedSource-compatible split (two renames). Every moved type kept its
namespace LightSide, so existingusing LightSide;code still resolves. Two utilities were renamed in the move —UniTextArrayPool<T>→ArrayPool<T>andRangeEx.WholeText/IsWholeText→RangeEx.All/IsAll, no aliases — everything else compiles unchanged. - AddedReusable inspector toolkit in Core. The
[SerializeReference]type picker (Selector,TypeSelectorDrawer,ManagedReferenceTypeMenu), reorderable styled lists (StyledListUtility,ListReorder), and section/vector/padding widgets that power UniText's inspectors are now a Core editor toolkit any project can drop into its own editors. - AddedGPU glyph-upload promoted to Core. The regional texture-upload backend (native plugin renamed
unitext_gpu→lightside_gpu) became a publicGpuUploadAPI inLightSide.Core/Runtime/Gpu, rebuilt around a device-epoch/device-reset-aware pool with a synchronous WebGL2 bridge — shared infrastructure across LightSide packages. - AddedBuilt-in profiler (advanced/developer tooling). Core ships
Prof— a lock-free per-thread Enter/Exit sink that reconstructs the call tree with self-time and per-method allocation — driven byProfWeaver, a UnityILPostProcessor(Mono.Cecil) that auto-wraps method bodies, inert unlessPROF_ENABLEis set and the assembly is allowlisted (Window > LightSide > Profiler Weaver→ProjectSettings/ProfWeaver.txt).ProfSampleradds a zero-per-call statistical backend for IL2CPP players (a native plugin walks the real stack into a lock-free ring), and development-build captures stream to the editor over the player connection intoWindow > LightSide > Profiler. - AddedOne LightSide editor menu. Shared tools consolidated under
Tools/LightSide/— the Noise Generator moved there fromTools/UniText/, joined by a newLog Zoneswindow (live on/off panel for theCatlogging system's zones). - Added
UniTextSelectable: read-only text selection for both Canvas (uGUI) and world-space text. Click to place a caret, double-click to select a word (a drag continuing from it extends by whole words), triple-click a paragraph, drag to select, Shift to extend, right-click/long-press to select-word-and-open-menu, keyboard Copy and Select-All while focused. Public mutators (SetCaret,SetSelection,ExtendSelection,SelectWord/SelectLine/SelectParagraph,SelectAll,ClearSelection,DragSelectionHandle, each returningbool) plusGetSelectedTextand gesture drivers (HandlePrimaryClick,DispatchTap/DispatchDoubleTap/DispatchTripleTap,BeginDrag/UpdateDrag/EndDrag). Single-selection-per-document with EventSystem-driven defocus. - Added
UniTextEditable: editable text without input-field chrome — a sibling ofUniTextSelectableon the same GameObject. ImplementsITextDocumentandISavedStateProvider. Standalone it tracks the text size through the text component'sILayoutElement— add aContentSizeFitteror place it under a layout group; wrapped byUniTextInputFieldfor the form-field UX. All OS input is delivered independently of Unity's input system. - Added
UniTextInputField: the form-field component, composingUniTextEditable+UniTextSelectable+UniTextBaseon a child editing surface inside anImagebackground and aRectMask2Dviewport. ImplementsILayoutElement+ILayoutGroupto grow with content up toMaxHeight(then scroll internally; on uGUI 2.6+ the cap is also published asILayoutElement.maxHeight), preserves scroll position across re-enable and list recycling, and reports its box screen-rect to native overlays. Field-level focus surface (ActivateInputField/DeactivateInputField,IsFocused, staticActiveField) and events (Focused,Defocused,EditEnded); an I-beam hover cursor that yields to occluding controls. Content typing is composed from input behaviors/filters/validators, not a fixed content-type enum. - AddedZero-alloc text access:
CharCount/CodepointCount,CopyTextTo(Span<char>),TextEquals(ReadOnlySpan<char>), with surrogate-pair-safe codepoint indexing — poll and compare a field every frame without touching the GC (theTextgetter is the opt-in allocating path). - Added
TextSelection(readonly struct):Anchor/Focus/Affinityover codepoint indices, withIsCollapsed,Start/End/Length,Clamp, aCaret(index, affinity)factory, and full value equality. Direction is implicit in anchor/focus ordering, modeled on the W3C Selection API / FlutterTextSelection. - Added
CaretAffinity(Downstream/Upstream): one unified hint that disambiguates caret rendering at both soft-wrap line breaks and LTR↔RTL BiDi run boundaries. - AddedBiDi-aware hit-testing (
SelectionHitTest,UniTextBase.HitTestCaret/IsOverText): edge-snapped caret placement (left half of a glyph → cluster N, right half → N+1), closest-edge resolution with swapped left/right semantics inside RTL runs, grapheme-cluster snapping, and a web line-box "over text" model for the I-beam affordance. Line resolution binary-searches a per-line advance prefix and the within-line scan touches only that line's glyphs — a click costs O(log lines + line), never the document. - AddedWord/line/paragraph boundaries: UAX #29-aware word segmentation with VS Code / Windows / macOS Ctrl/Option+Arrow semantics — apostrophes in words (
don't, and the curlydon’t), digit separators (3.14,1,000), Katakana as its own script run, Han ideographs and Hiragana broken per cluster, Thai/Lao/Khmer/Myanmar snapped to dictionary word boundaries (Thai dictionary in the box; register others viaUniTextSettings.Dictionaries), emoji/ZWJ/variation-selector clusters never split. - AddedSelection events:
SelectionChanging(vetoable —Cancelor reassignProposedto clamp a proposed change out of an atomic pill or read-only region),SelectionChanged(with a reason), and a 14-valueSelectionChangeReasonopen-string vocabulary (select.pointer,select.word,select.extend,select.clamp, …) matching the CodeMirror 6 convention. - AddedGrapheme-cluster caret movement: one arrow press or one Backspace crosses a whole emoji, ZWJ sequence, or base+combining-mark cluster — caret positions always sit on grapheme boundaries.
- AddedVertical movement with a preserved column anchor: Up/Down/PageUp/PageDown keep a desired-X column across consecutive vertical moves, reset on any horizontal/word/edit/click.
- AddedRTL-base-aware horizontal movement: in an RTL line, Left/Right step toward logical start/end (Android
getParagraphDirectionconvention); word movement obeys the same flip. The empty last line after a trailing newline follows the preceding paragraph's direction, matching mainstream editors. - Added
InputCaretRenderer: the caret component — aRectMask2D-clipped quad with blink-on-interval, blink-idle-timeout-then-steady, immediate geometry on type (no one-frame lag), and configurable width/color; subclass it (overrideOnPopulateMesh) for a block, underline, or gradient caret. - AddedEdit operations on
UniTextEditable:InsertText(string andReadOnlySpan<char>),DeletePrevious/DeleteNext,DeleteSelection,DeleteWordPrevious/DeleteWordNext,DeleteToLineStart/DeleteToLineEnd,TransposeCharacters(carries adjacent formatting through the swap),SelectAll,Select,MoveCaretTo. Delete/transpose boundaries are computed over the document with a bounded UAX #29 window — never a stale previous-frame layout — so key-repeat and frame hitches can't delete the wrong cluster. - AddedUndo/redo:
Undo/Redo/CanUndo/CanRedo/ClearUndoHistory, with word-aware token-class coalescing (the VS Code / CodeMirror 6 word↔separator rule, held-Backspace merged, Replace never coalesced), a configurable time window (UniText settings, default 0.5 s), and an allocation-free store — undo text lives in a sharedchar[]referenced by slices, capped by the settings' undo memory limit (default 1 MB, oldest-first eviction, the newest entry always survives; 0 = unlimited). Restores the pre-edit selection on undo; multi-step commands (styled typing, IME commit over a selection) group into one undo step. - AddedProgrammatic set-text with explicit undo policy:
SetText(replace, reset selection, clear history) vsSetTextProgrammatic(value, recordUndo = false, preserveHistory = false, reason = null)—preserveHistory:truerebases existing undo entries through the swap's minimal diff, so a collaborative/network echo that only appends keeps the user's local undo (CodeMirroraddToHistory:false);reasontags the event (e.g.sync.network). - Added
ITextDocument: a read-only, codepoint-indexed document abstraction (CodepointCount,CharCount,Version,CopyCodepointRange,GetCodepointAt) that validators, find/replace, and accessibility consumers depend on instead of the concrete editor. - Added
EditApplied+EditShape: every mutation raisesEditApplied(EditShape)synchronously (afterVersionadvances, before the frame-coalescedTextChanged) carryingStart/Removed/Inserted/Delta/MapIndex— the zero-alloc damage hint for keeping indexed state (search results, CRDT positions, highlight ranges) in sync without rescanning. - AddedChange-reason vocabulary (
TextChangeReason): open dotted-prefix strings carried byDocumentChanged—input.type,input.type.compose,input.paste,input.delete[.backward|.forward],input.cut,input.format,input.restore(input.*is exclusively user input),program.setfor programmatic mutation,sync.networkfor collaborative sync, andedit.stylefor formatting-command tag rewrites — so integrators react differently to typing vs paste vs programmatic vs IME edits with one prefix check. - AddedEvents:
TextChanged(zero-alloc),ValueChanged,DocumentChanged(with reason),SelectionChanged(frame-coalesced),Submitted,Cancelled,Focused/Defocused,CompositionStateChanged,TouchKeyboardVisibilityChanged,ValidationChanged. - Added
ISavedStateProvider:SaveState/RestoreStatepersist text, selection, affinity, and scroll into aunitext.*-prefixed bundle for host-driven view recreation (virtualized lists, mobile process-death restore, Web bfcache). - AddedInput sanitization: lone surrogates and C0/DEL control chars dropped,
\r\ncollapsed to\n, NUL rejected, with a 1,000,000-char paste safety cap — the field is safe to point at arbitrary pasted data. - Added
GraphemeCountCache: grapheme-cluster count cached by documentVersion, predicting a pending edit by re-segmenting only a boundary-safe window — live counters and grapheme length limits stay O(window) per keystroke instead of a full rescan. - Added
EditActionenum: the full editing action set — movement (char/word/line/page/document), selection variants of each, delete (prev/next/word/line),TransposeChars,InsertNewline/InsertTab, Copy/Cut/Paste/PasteAsPlain, Undo/Redo, Submit/Cancel — plusIgnore(consume a key and block its platform default) and range constants for permission filtering. - AddedPlatform-aware key resolution (
PlatformKeySemantics+ a built-in key map): macOS (Cmd shortcuts, Option word-nav, Cmd+←/→ line, Cmd+↑/↓ document, plus default Emacs bindings Ctrl+A/E/F/B/P/N/K/T/D/H) vs Windows/Linux (Ctrl shortcuts, Ctrl+Y redo, Ctrl+Home/End, Shift+Insert/Ctrl+Insert/Shift+Delete, AltGr-safe Ctrl+Alt), with Cmd-vs-Ctrl resolved at runtime (a macOS-browser WebGL build and an iPad hardware keyboard get Cmd). Read-only mode runs only navigation/selection/Copy/Submit/Cancel; mutating actions are suppressed during IME composition. - AddedNon-destructive composition: in-progress IME text renders without being written to the document until the IME commits, and the commit lands as a single undo entry. CJK candidate windows, dead keys (´+e→é), and accent menus work correctly; composition renders unmasked in password fields. The overlay commits synchronously on defocus/
SetTextso text is never lost when the user taps away or switches apps, and late echoes from a killed session are discarded. - AddedClause model (
CompositionData,CompositionClauseStyle): per-clause attributes (macOS/iOSNSMarkedClauseSegmentruns, Android composing spans) are normalized into one neutral clause vocabulary —Unconverted/TargetConverted/Converted/TargetNotConverted/Error, the Windows IMMATTR_*taxonomy — so integrators reason about conversion state without per-platform code; a platform that delivers no clause detail reports oneUnconvertedclause. - AddedCandidate-window placement: the caret position is reported in native client space (
UniTextNativeInput.SetCursorScreenPos, Editor Game-View letterbox-compensated), andSetImeCaretScreenRectpins the window for inline Scene-view editing — so the candidate/emoji/accent window sits on the caret in a build, the Editor Game View, and the Scene View. - AddedText-input-context queries answered from UniText's own layout:
TryGetCharRangeRect(first-rect-for-range),HitTestChar(closest position to point),WritingDirection(per-codepoint UAX #9 levels), and a grapheme/wordTokenizerQuery— feeding macOSNSTextInputClient, iOSUITextInput, dictation, VoiceOver character/word reading, and candidate placement from real shaped glyphs, not an approximation. - Added
ITextInputContext: the neutral document-context seam the native backends query.UniTextEditableinstalls itself while active; a console, terminal, or custom text host installs its own context and receives full native IME.CompleteComposition/CancelCompositiongive hosts programmatic commit/discard; IME-driven caret moves (Gboard spacebar-swipe,InputConnection.setSelection) are applied as input. - AddedAndroid IME context mirror: the focused document and selection are mirrored into the platform
InputConnection— the whole text when ≤ 1,024 UTF-16 chars, else a ~512-char window centered on the selection and snapped to grapheme boundaries, coalesced to one push per frame and cached by document version — so Gboard suggestions, autocorrect, and spacebar cursor control see real surrounding context with zero per-keystroke allocation; pull-model platforms (Windows, macOS, iOS) skip the push entirely. - Added
UniTextNativeInput: delivers OS key, text, composition, selection, and keyboard-visibility events independently of Unity's input system (noEvent.PopEvent), so editing works regardless of the project's Active Input Handling (Legacy / New Input System / Both) — removing the single biggest friction point of every Unity input-field asset. Control keys arrive only via key-down; printable text only after the OS resolves layout/dead-keys/IME. Cross-platformNativeKeyCodeand[Flags] NativeModifiers. - AddedWindows Text Services Framework text store: the new
UniTextNativeInputWindows.dll(x86_64 + ARM64) hosts anITextStoreACPdocument, so the Win11 emoji/GIF panel (Win+.), clipboard history (Win+V), and modern TSF IMEs insert into the field with the candidate window anchored at the caret — even under the New Input System. - AddedPluggable input backends (
INativeInputBackend,RegisterBackend): a priority-ranked registry with aManagedInputBackendfallback registered on every platform, so a missing/failed native plugin (or an unsupported platform) still types, and studios can add first-class input for consoles/TVs without forking the package. - Added
NativeKeyboardConfig: soft-keyboard traits the OS reads on show —KeyboardType,ReturnKeyType,AutoCapitalization,AutoCorrection,SpellChecking,AutofillHint, iOS smart-quotes/dashes/insert-delete opt-out,KeyboardAppearance, return-key auto-dim, Done toolbar, password rules,AndroidImeFlags, and raw iOS keyboard/return-key overrides. Every option defaults to the user's OS preference. (iOS coverage is full; Android maps type/caps/correction/spell/return/ime-flags; WebGL maps inputmode/enterkeyhint/autocapitalize/autocorrect/spellcheck/autocomplete.) - Added
NativeFieldOverlayStyle/NativeFieldHandle: opt into the OS's own field (UITextField/UITextView,EditText, DOM<input>/<textarea>) for system selection handles, autofill chrome, voice input, and per-platform styling — the only route to true native UX inside a WebGL browser sandbox.NativeFieldHandlereturns the raw native-view pointer for further customization (functional on iOS today). - AddedKeyboard-avoidance data (
KeyboardEvent,KeyboardEventPhase,KeyboardEasing): will-show/animation-progress/did-show/will-hide/did-hide/will-change-frame phases with duration, easing, and a frame-synced animation fraction where the OS exposes it (Android API 30+), plus a documented client-side fallback (iOS, Android <30, WebGL). - AddedSoft-keyboard action button (
NativeEditorAction): the keyboard's action key is delivered as an open vocabulary —Submit/Next/Previous/Newline, with Go/Search/Send/Done all deliveringSubmit— so a chat composer submits and a form field advances focus, degrading to a synthesized Return with no subscriber. - AddedNative plugins shipped and CI-built: Android
UniTextInput.aar, macOSUniTextNativeInputMacOS.dylib(anNSTextInputClientview), WindowsUniTextNativeInputWindows.dll(x64 + ARM64), iOSUniTextNativeInput.mm(aUITextInputview), and WebGL.jslib— full native IME and soft-keyboard support in the box, no external SDK. - AddedAutomatic hardware cursor: an I-beam appears over text and a hand over links, from a 25-shape internal cursor table mapped to per-platform IDs (Windows
OCR_*, macOSNSCursor, WebGL CSScursor; Linux keeps the default arrow); an interactive range's hover cursor is configurable per modifier in the inspector (serializedhoverCursor, default: link hand). - AddedMulti-format, multi-channel clipboard: one Copy writes many formats atomically and Paste auto-selects the richest understood one —
ClipboardFormat.PlainText,Html,Markdown,UniTextSource(lossless UniText vendor format),Url, andPng/Jpeg/Gifimages.ClipboardItempairs a MIME-stringClipboardFormat(withCustom(string)andTryDetectImage) with bytes; bypassesGUIUtility.systemCopyBufferon the six supported platforms (a plain-text fallback covers anything else). - AddedFormat adapters (
IClipboardAdapter): per-format translators between UniText markup and external formats, ordered byPriorityon paste (lossless UniText source 100, HTML 50, Markdown 40, plain text 0 — the richest present format wins). Four shipping stateless built-ins:UniTextSourceClipboardAdapter,TagHtmlClipboardAdapter,MarkdownClipboardAdapter,PlainTextClipboardAdapter. - AddedData-driven modifier serialization schemas (
ModifierClipboardSchema,ClipboardModifierBindMap): each modifier type declares how it maps to HTML (semantic element, CSS-on-<span>, or attribute) and Markdown (marker or link), registered per type with no assembly scan (IL2CPP-safe). Pre-registered for the 15 shipping modifiers (bold, italic, underline, strikethrough, color, size, font, link, language, line-height, letter-spacing, script position, small-caps, upper/lowercase). Register a custom modifier's mapping with oneClipboardModifierBindMap.Register<T>call — this is the per-modifier extension seam, not custom adapters. - AddedWord / Outlook / Google-Docs interop: the HTML paste parser is a single forward pass with an explicit element stack — it strips leaked
<style>/<script>blocks, collapses whitespace the way CSS renders it, resolves entities, closes mis-nested tags, and honors the Windows CF_HTML fragment header — so pasted rich text comes in clean, not as a wall of CSS and blank lines. CSS unit bridging (CssValueFormat) convertspx/pt/rem, unitless line-heights, andrgb()/rgba()colors into UniText's own units on paste and emits valid CSS units on copy — including Google-Docsfont-weightconventions. - AddedLossless UniText round-trip (
ClipboardFormat.UniTextSource,application/vnd.lightside.unitext): a JSON fragment keyed by modifier signature re-resolves each span by modifier identity and re-emits it in the destination field's own syntax — copy between two of your fields and keep every style byte-perfect, even styles no HTML or Markdown could express. - AddedStyle-safe partial copy: copying part of a styled run re-synthesizes the open or close tag the selection cut off, in the pair's original syntax — a half-selected
<b>span (or*bold*marker run) copies as valid, still-styled markup instead of a dangling tag. - AddedPaste policies & commands:
PlainTextPastePolicy(Auto/Literal/Parse),Paste(),PastePlain()(paste-and-match-style),Cut()/Copy(), asyncPasteAsync()/PastePlainAsync()(required for programmatic paste on WebGL), andPasteFromItems(IReadOnlyList<ClipboardItem>). - AddedMedia clipboard (
MediaContent,MediaSource,IMediaClipboardProvider, theMediaReceivedhook): receive pasted images and files before the text channels run, and read their bytes per platform (CF_HDROP paths on desktop,content://on Android, browser blob on web; large files off-thread) — paste a screenshot into a chat field and upload it. - AddedWindows screenshot & image interop: copies write the registered
PNGformat plus a synthesizedCF_DIBV5, and reads convertCF_DIB/CF_DIBV5back to PNG — so a copied image pastes into Paint/Word and a PrintScreen capture pastes straight into a field. - AddedProvider abstraction (
UniTextClipboardfacade,IClipboardProvider/IAsyncClipboardProvider): swap the backend for tests or a custom platform behind one interface; staticGetText/SetText/GetTextAsynccover the plain-text fast path. - AddedSix native backends: Windows CF_HTML + registered formats + CF_DIBV5 + CF_HDROP; macOS
NSPasteboardmulti-format writes via the Obj-C runtime (no compiled plugin); iOSUIPasteboarditems; AndroidnewHtmlText+ a bundledcontent://image provider (no androidx dependency); WebGL Async Clipboard API + Web Custom Formats; Linux xclip/xsel/wl-copy — a real OS clipboard everywhere, not Unity's plain-text-only buffer. - AddedHostile-input hardening (
ClipboardBudget): every paste parser caps at 4 MB / nesting depth 64 / 1,000,000 chars and stays linear on degenerate input, so a malicious clipboard payload can't hang or OOM the app. - AddedApply/toggle styles over a selection, in the style's own markup syntax:
ApplyStyle<T>,ApplyStyleRange<T>,SetStyle<T>(range on/off),ToggleStyle<T>,ToggleStyle(BaseModifier, …),RemoveStyleRange<T>,ClearFormatting,InsertObject<T>. Reference-only — a style the field has no configured rule for is a no-op; nothing is ever minted. Toggle-off splits runs so the style survives outside the range, with single-undo semantics. - AddedPending typing styles (ProseMirror stored-marks model): at a collapsed caret a style command stores a pending style that wraps the next typed text; caret movement discards it, and
ArrowKeyEscapesFormatting(off on a bare editable;TextFormattingBehaviorturns it on) peels one formatted range at a time at its edge — the ProseMirror/Lexical mark-boundary model. - AddedToolbar-state queries:
IsStyleActive<T>(),TryGetStyleParameter<T>(out …)(falseon a mixed selection),ModifiersAtCaret, and a frame-coalescedCaretContextChangedevent delivering aCaretContextdiffed by modifier signature — bind a formatting toolbar's pressed/color state without per-frame polling. - Added
MarkupVisibility—Hidden(tags hidden and atomic but preserved so styles round-trip),RevealActiveRange(reveal the markup of the span under the caret, like a code editor),Raw(literal source view). Caret, selection, hit-testing, and deletion stay correct while tags are hidden — deleting the last styled char removes the whole<b>…</b>pair in one undo step. - Added
TypingMarkupPolicy(Parse/Literal): markup the user types by hand is either recognized or escaped to literal text; affects only keystroke/IME input, not formatting commands or paste. - Added
ChromeRule+IMarkupSelector: style the visible tag delimiters when revealed (AnyMarkup/ByModifier/ByRule, by specificity), with tag characters protected from the surrounding document style; runtime-mutable viaMarkupChrome+RefreshMarkup. - AddedParse rules describe and re-emit their own syntax (
ParseRule.Identity,SourceToken,MarkupTriggers/ScanTriggers,CanWrap,Apply, plus the literal-escape contractEscapePrefix/IsEscapable): this is the mechanism that lets one formatting command drive a tag rule (<b>) and a Markdown marker rule (*bold*) alike — the core "it is now an editor" story. - AddedStyle identity by modifier signature: toggling Bold finds the field's own
<b>style instead of minting one — styles are matched by concrete modifier type, a composite by its ordered child types ({Bold,Italic} distinct from {Italic,Bold}) — and composite styles round-trip losslessly through the clipboard. - AddedMulti-syntax binding (
UniTextBase.EnsureStyleFor): one semantic modifier reacts to multiple input syntaxes (<i>,*…*, pasted<em>), merged into aCompositeParseRuleon demand — no duplicate style entries. - Added
IFormatStyleSource/ReferencedStyle: the serialized seam a formatting command toggles — pick a modifier in the inspector and a toolbar button applies it in that style's own syntax, inert on fields that lack it. - Added
TriggerWordParseRule: built-in@mention/#hashtagauto-detection with a configurable trigger character, runs at negative priority so explicit markup wins. - AddedLink rules carry a default style:
MarkdownLinkParseRuleandRawUrlParseRulegained aDefaultParametermerged positionally behind the matched URL, so an auto-detected or[text](url)link applies a fixed visual preset (e.g. a Link;Color;Underline composite) configured on the rule. - AddedMarkdown markers follow CommonMark flanking rules (
MarkdownWrapRule): a marker opens only when left-flanking and closes only when right-flanking (CommonMark §6.2), and_-markers never toggle inside a word —2 * 3andsnake_case_namestay literal. In 2.x every marker occurrence toggled. - AddedPer-style enable toggle (
Style.Enabled, an eye button on each style row): disable a style non-destructively — skipped by the parser, rendering, and every style query while its configuration is preserved; re-enabling restores its authored precedence among overlapping styles. - AddedComposable, serialized, user-extensible behavior system (
InputBehavior): drop-in policy units attached to a field as a[SerializeReference]type-picker list, authored like style modifiers — subclass the base, add serialized fields, overrideOnEnable/OnDisableto subscribe the editor's extension events. Teardown is fully reversible (an internalSaved<T>slot restores host state on disable). - AddedDocumented transaction seam: the four hooks a behavior subscribes —
InputFilter(InputEdit),KeyResolver(KeyResolve),KeyboardResolver(KeyboardRequest),MediaReceived(MediaContent).InputEditcarries the inserted text, target range, caret, and aTextChangeReason, with a stickyRejected— so a whole-field mask can rewrite the entire transaction. Hooks run in order, each seeing the previous result, only on committed text (never IME composition, undo replay, or programmatic writes). - AddedRuntime hot-swap:
AddBehavior,RemoveBehavior,GetBehavior<T>(),GetBehaviors<T>(List<T>)flip a field's policy live; shared presets attach viaAddBehaviorPreset/RemoveBehaviorPreset. - Added
InputBehaviorPreset: a reusableScriptableObjectfield archetype (chat composer, password field, form field). Each editor instantiates a runtime copy so per-instance state never leaks back to the asset; editing the preset asset raisesChangedand rebuilds every live field using it in place. - AddedBuilt-in behaviors (15 concretes;
KeyboardBehaviorandMediaInputBehaviorship as ready-made bases for authoring keyboard-lifecycle and media-handling behaviors):PasswordBehavior(caret-aligned masking + secure entry + copy blocking + reveal toggle),SingleLineBehavior,LengthLimitBehavior(grapheme-counted, boundary-safe),CaseTransformBehavior(upper/lower/title),InputMaskBehavior(live(###) ###-####-style formatting, paste- and mid-field-edit aware, withRawText),SubmitKeyBehavior(Enter or Ctrl/Cmd+Enter),TabKeyBehavior,NativeKeyboardBehavior,NativeFieldOverlayBehavior,KeyboardAvoidanceBehavior(animated lift, canvas-scale and projection correct),TextFormattingBehavior,AutoValidateBehavior,LinkOnPasteBehavior,StripFormatOnPasteBehavior,CaretContextBehavior. - Added
TextFormattingBehavior: a field's whole formatting layer on one behavior — a configurableCommandslist (default B/I/U viaFormatCommand), a clear-formatting shortcut (Ctrl/Cmd+\ by default),Toggle(string)/Toggle(int)for toolbar buttons, and ownership ofMarkupVisibility/RichPaste/ paste policy /TypingMarkup/ arrow-key escape / tag chrome while enabled. - Added
CaretContextBehavior+StyleStateHandler: wire a Bold button's highlight with zero code — an edge-triggered handler fires aUnityEvent<bool>only when "would typing now produce this style?" flips. - Added
InputFilterBase: a self-wiring behavior that rejects characters as typed by judging the whole post-edit span (surrogate/grapheme-safe, native-multi-char aware) and requests a preferred mobile keyboard. Built-ins:IntegerFilter,DecimalFilter,AlphanumericFilter(Unicode letters + ASCII digits),EmailFilter,NameFilter(letters + space/hyphen/apostrophe). Numeric filters exposeAllowNegative, which both accepts a leading minus and switches the requested keyboard to one that has a minus key. - Added
InputValidatorBase+ValidationState: judge the whole value and publish an open-string status (ValidationStatus.Invalid/Pending; valid = empty status) + message, scheduled byAutoValidateBehavior(OnValueChanged/OnUnfocus/OnSubmit/Always) and wired straight into the field's error visuals. Async-capable (Pending). - Added
TextMeasure/TextLengthUnit: measure and boundary-safe-truncate text byGraphemes(default — a family emoji counts as one),Utf16Units,Utf8Bytes, orCodepoints;LengthLimitpublished to counters — never the wrong numberstring.Lengthgives. - AddedDecorator system (
FieldDecorator): composable field chrome reacting to a pushedFieldStatesnapshot (IsEmpty,IsFocused,IsComposing,LengthLimit,Validation,Box) with attach/dispatch/tick/detach lifecycle; an idle decorator holds no per-frame subscription. Author your own via the type picker. - AddedBuilt-in decorators:
PlaceholderDecorator,FloatingLabelDecorator(Material-style animated label with easing curve/duration, snapping instantly under Reduce Motion),SupportingTextDecorator(helper text that swaps to the validation error message/color),CharacterCounterDecorator(grapheme-awarecount/count/limit, recolors at the limit, cached by document version). - Added
UniTextContextMenu(ITextContextMenu): a data-driven, bring-your-own-UI context menu — you build and style the panel/buttons in the scene, the component wires controls to commands, hides inapplicable items, positions and clamps the panel on-screen with an edge-aware pivot, and dismisses on outside click via a generated full-screen blocker. Visibility runs through aCanvasGroup(never deactivating the GameObject) so aFocusGuardon the panel keeps the editor focused. - AddedMenu items (
ContextMenuItem,BuiltInContextMenuItems):Cut/Copy/Paste/SelectAllplus customActionContextMenuItem(aButton→UnityEvent) andToggleContextMenuItem, each with per-item applicability rules (onlyWithSelectionon the custom items). Standard commands route through the last-shown presenter so one menu instance safely serves every field; custom items fire their ownUnityEvent. Raised on right-click or long-press viaUniTextSelectable.RequestContextMenu, or subscribeContextMenuRequested/ContextMenuDismissRequestedfor a fully custom or OS-native menu (capability-gated — no copy from a password field). - AddedGesture recognition (
TouchGestureRecognizer, a public state machine now in LightSide.Core): platform-standard single/double/triple tap, long-press, and drag — disambiguating drag-to-select from swipe-to-scroll by tap count, with no single-tap flicker under a double-tap — and time-scale independent. Thresholds are serialized onUniTextSettings, dp-based and DPI-aware: long-press 0.5 s, drag slop 10 dp, multi-tap window 0.3 s / slop 100 dp, desktop multi-click 0.5 s / 8 dp (matching AndroidDOUBLE_TAP_*, FlutterkDoubleTap*, WindowsGetDoubleClickTime). - AddedMagnifier loupe (
IMagnifier, shippedDefaultMagnifier): an iOS-style magnification bubble during long-press caret placement and handle dragging, rendered through a secondary camera into a render texture (Built-in RP viaCamera.Render, URP/HDRP viaSubmitRenderRequeston 2023.1+), with a documented graceful hide where capture isn't possible. Replaceable via the interface. - AddedSelection handles (
ISelectionHandles+IInsertionHandle, shippedDefaultSelectionHandles): draggable teardrop start/end handles that extend the selection cluster-by-cluster — never collapsing, swapping roles past the fixed endpoint, showing the loupe during their drag — plus a caret knob that drags the collapsed caret and taps to reopen the menu, all rendered in a canvas-level overlay so the field mask never clips them. Replaceable via the interfaces. - AddedReplaceable touch UI:
SelectionHandles,Magnifier, andContextMenuare component slots onUniTextSelectable— assign the shipped defaults, your own custom-rendered components, or OS-native ones without subclassing;UniTextEditableonly drives what's assigned. - Added
UniTextPasteControl: on iOS 16+ overlays a nativeUIPasteControlto bypass the system paste-permission prompt and capture plain/HTML/lossless-UniText in one tap; falls back to aButton → Paste()elsewhere, raisingPastedeither way; display and corner styles (PasteControlDisplayMode/PasteControlCornerStyle) map to Apple's UIPasteControl configuration. - Added
TextPointerEvent: a consumable, reused (zero-alloc) pointer event onUniTextBasecarrying the hit result, aPointerTrigger(PrimaryClick/SecondaryClick/LongPress/Hover), aPointerKind(Mouse/Touch/Pen, resolved authoritatively from the new Input System), screen position, camera, and live[Flags] PointerModifiers. SettingConsumedclaims the click so a parent ScrollRect never sees it. - AddedNew events on
UniTextBase:ContextRequested(right-click or touch/pen long-press, mirrors HTMLcontextmenu/WinUI/SwiftUI),TextLongPressProgress(0→1 for loupe/ring feedback, mouse holds excluded),PointerPressed/Released,PointerEntered/Exited(topmost-raycast model — an occluding child correctly suppresses the hover). No per-frame cost when unsubscribed; Canvas and world-space text raise the same events. - AddedDrag-to-select events on
UniTextSelectable(SelectionDragStarted/SelectionDragUpdated/SelectionDragEnded, anchored at the original press position) — kept offUniTextBaseso a plain label never captures drags from an enclosing ScrollRect. - Added
UniTextHighlights: named highlight layers over any text component — the CSS Custom Highlight model (selection, find-in-page, hover feedback, custom range marks). One-liner entry:UniTextHighlights.GetOrAdd(text).GetOrCreate("find-results"); layers can also be authored in the inspector. All layers of one render side draw as a single mesh on both Canvas and world-space text; an idle component costs nothing per frame. - Added
HighlightLayer: codepoint ranges (Add/SetRange/SetRanges/Clear) painted as a merged union (overlaps never double-blend). IntegerPrioritywith documented anchors (HighlightPriorities:Interactive−10,Custom0,Selection100,FindCurrent200,Diagnostics300),Order(behind/above text),Visible, animation-friendlyOpacity(re-tints in place, no geometry rebuild), and edit-sticky ranges —Stickiness(GrowsAtEdges(Monaco),Shrinks,RemoveOnOverlap) maps ranges through every edit of a siblingUniTextEditable, tracked in document space and remapped under hidden markup (publicMapThroughEditlets an external document backend — a CRDT, a network host — drive the same mapping). - Added
HighlightStyle: the layer's presentation —Paint(an inline colour or a solid/gradient swatch from the paint catalog),Height(LineBox/Content/LineAdvance), padding, corner radius, merge threshold, andBoxBreak(Slice/Clone/Block— Block collapses a range to one rounded rect for the code-block look). - AddedHandle-based render backend (
TextHighlightRenderer,HighlightGroup,HighlightRect): individually mutable rects with per-rect color, rounded corners, gradient paint, and a freeCustomData(uv1) channel for authoring custom highlight shaders — yet a side's layers collapse into one draw call. Steady-state repaints mutate live rects in place, and a stale handle throwsObjectDisposedExceptioninstead of silently aliasing another rect. - AddedFind & read API on every text component:
UniTextBase.FindAll(query, comparison, results)— allocation-free for queries up to 64 chars, codepoint-correct, case-sensitive or -insensitive per the passedStringComparison(bundled Unicode simple case mappings; culture values compare ordinally), returning ranges that feed highlight layers andGetRangeBoundsdirectly — plus theCodepointCount/RenderedCodepointsread surface. - AddedInteractive ranges are now configuration, not a subclass:
InteractiveModifieris a concrete, inspector-pickable modifier (new InteractiveModifier { RangeType = "mention" }) running a per-rangeNormal/Hovered/Pressedstate machine, consuming pointer events, and exposing six events (RangeStateChanged,RangeClicked,RangeContextRequested,RangeEntered,RangeExited,RangeLongPressProgress) plusGetRangeStateand a protectedIsRangeEnabledoverride to disable ranges dynamically (visited links, permission gates) — the web LVHA / UIKitUITextItem/ AndroidClickableSpanmodel with no registry. - AddedRange stylers (
RangeStyler): the presentation seam assigned toInteractiveModifier.Styler— state transitions, activation flashes, and long-press progress arrive as hooks, typically drawn through highlight layers; idle stylers hold no per-frame subscription.StateHighlightStyleris the shipped default: animated hover tint, instant pressed fill, activation flash, optional long-press buildup — allOpacityfades, no geometry rebuild — plus aNormalEnabledpersistent chip layer under the state layers (the Material state-layer model: a mention chip is oneInteractiveModifierwith this on, rebuilt at parse rate, never per frame). - AddedBuilt-in interactive modifiers: the new
SpoilerModifier(Discord/Telegram tap-to-reveal covers —RevealChanged,SetRevealed/ConcealAll/IsRevealed, per-range reveal reconciled across re-parses, animated covers via the shippedSpoilerCoverStyler) andLinkModifierrebuilt on the state machine: base colour/underline styling moved out to composition, an iOS-style grey pressed flash via its defaultStateHighlightStyler(its 2.xLinkClicked/LinkEntered/LinkExitedevents andAutoOpenUrl— on by default — carry over) — both worked examples for authoring your own stateful interactive text. - AddedSelection style:
UniTextSelectable.SelectionStyle(aHighlightStyle) drives the reserved"selection"layer atHighlightPriorities.Selection— recolor, round, or gradient-fill the selection bar from one serialized field. - AddedOne paint system for every layer (
UniTextPaintscatalog,PaintSwatch,IPaintProvider): a named swatch is a solid color, aGradient, or aTexture2D, referenced straight from markup (<fill=ember>,<stroke=gold>) or configured on a modifier. The same paint drives fills, strokes, shadows, glows, and underlines. - AddedTexture-filled text: any layer can be painted with an image — chrome, marble, foil, animated sheets — with
PaintFit(Stretch/Contain/Cover/Tile). - AddedGradients rebuilt per-pixel: 2.x evaluated a gradient at the four corners of each glyph quad; gradients now sample a shared ramp atlas per fragment, so radial and conic shapes stay correct inside every glyph. New
PaintMappingframes (Blockacross the text,Lineper line,Glyphper glyph,Rangeacross the styled span) plusscale/offsetjoin the 2.xLinear/Radial/Angularshapes andangle. - Added
FillModifier: fills the glyph interior with a paint; the first fill claims the suppressed base quad (no extra geometry), additional fills stack above it. - Added
StrokeModifier(replaces the old outline blob): a true rim band with continuousAlign(−1 inside → 0 centered → +1 outside; legacyinside/center/outsidekeywords still parse),Width,Softness, and aCornerStyle(artifact-freeRound, orSharpmitered up toMiterLimit— shared by the layer base, so shadows and glows get the same corner treatment) — from an MTSDF two-distance read, works with the fill off, and takes any paint. - Added
GlowModifier(paintable soft halo,Radius),ShadowModifierrebuilt with independentOffset/Blur/Spreadand any paint (a real blurred/spread/gradient shadow, not a hard offset copy), andInnerShadowModifier(inset shadow via a second SDF tap — embossed/letterpress depth, crisp at any size). - AddedPaintable decorations (
BaseLineModifier): underline & strikethrough gained per-range paint (color/gradient/texture, defaulting to the text fill like CSStext-decoration-color: currentColor), and the 2.x line-style set (solid/double/dotted/dashed/wavy, thickness, offset, skip-ink) gained serialized defaults and public properties (Style,Thickness,Offset,SkipInk) — CSS Text Decoration Level 4. - Added
GlyphResolutionModifier: raises the atlas tile resolution of the glyphs in its range (ResolutionBoost0–2) for crisp large headings without bumping the whole font's atlas. - Added
UnitValue/UnitVector2: the px/em/%/delta parameter grammar became a serialized value+unit type — an inspector default now carries its unit, and every effect geometry parameter (stroke width, glow radius, shadow offset/blur/spread) takes px or em: pin to px for constant on-screen size, or em to scale with the glyph. - AddedSerialized defaults + public property + per-range override on every modifier: a bare tag (
<b>,<glow>,<color>) takes the inspector-configured default; an inline value overrides it — and each parameter is now a real C# property (ColorModifier.Color,BoldModifier.Weight/Mode,SizeModifier.Size). - Added
PaintLayerModifierextension point: fill/stroke/shadow/glow/inner-shadow all derive from one layer that resolves the paint, precomputes gradient/texture mapping per range, and stamps coverage quads — a custom effect gets swatch resolution and the full override chain for free, and a layer's overlapping ranges (nested tags) resolve innermost-wins once per codepoint (O(1) per glyph, not per glyph×range). - AddedPhase-driven model (
IPhaseDriven): a modifier's visual state is a pure function of itsPhaseand never advances itself — drive it from the drop-inUniTextPhaseDriver(free-runningSpeed, optionalUnscaledTime, scrubbablePhase), a tween, Timeline, anAnimator(PhaseAnimationHandler), or code. The same phase always renders the same frame, so scrubbing, rewinding, and multi-instance sync all work. - AddedEleven built-in glyph animations (up from one wobble in 2.x), each configurable per range and most staggered along the text by a per-cluster
Spread:WaveModifier,BounceModifier,PulseModifier,ShakeModifier,WobbleModifier,SpinModifier,PendulumModifier,FloatModifier, plus three effects that normally need custom shaders —GlitchModifier(RGB-split bursts),ScrambleModifier(hacker-style decode, grapheme-correct, no reflow), andRollingModifier(odometer / split-flap wheels — driveRolltoward 0 to settle). - AddedPer-glyph reveal animation (
RevealModifier.GlyphRevealing): the 2.8 typewriter modifier gains a mesh-build hook — each rendered glyph of a revealing range arrives as aRevealGlyphInfo(ordinal,count,front, fractionalProgress), so fade/scale/drop-in appearance is a few lines of quad mutation; while subscribed, the frontier cluster renders at fractional progress instead of popping in whole. - AddedAuthor your own (
GlyphParamModifier<TParams>+ the staticGlyphQuadhelpers): a phase-driven per-character effect in ~50 lines, riding the same driver/scrub/Timeline machinery as the built-ins;OnGlyphruns allocation-free during mesh generation, worker-thread-safe. - AddedWrite once, run everywhere: author a single
half4 UniTextEffect(UniTextFrag i)holding only visual logic and the template shells wire it into every pass — Canvas + world-space, Built-in + URP, SDF/MSDF/emoji. TheUniTextFragstruct exposessdfAlpha/signedDist/glyphUV/tileId/tileHash/userA/userB/positionWS, plus an optional vertex hook and a world shadow-caster program. Custom effects on world text — Built-in or URP — did not exist in 2.x. - AddedNew authoring templates: every bundled effect (Rainbow, Dissolve, Hologram) now ships as a Canvas shell and a world/URP shell over a reusable logic include, plus a blank
UniText_Effect-Example.hlslstarting point; shared building blocks live inUniText_EffectLib.hlsl. - AddedOne-click scaffolding (
Assets > Create > UniText > Custom Effect, renamed from Custom Material Shader): one click writes the full trio into the selected folder — the pipeline-neutral effect include plus pre-wired Canvas and World shader shells, include paths resolved and a unique shader name stamped in — so a new effect compiles for every pass the moment it lands. - AddedCold-glyph rasterization now costs the main thread almost nothing. The async GPU rasterizer (shipped in 2.10.0) is rebuilt end-to-end — Burst outline extraction and contour union, CPU-precomputed 2D binning, per-font batching — leaving the main thread only a short dispatch while the GPU fills the atlas asynchronously. Measured (Galaxy S21 FE 5G, Adreno 660, Vulkan, IL2CPP release build), cold-atlas fill of a 3,029-glyph multilingual NotoSans corpus (TextMeshPro/UI Toolkit filled 3,091/3,092): UniText ≈ 33 ms of main-thread dispatch vs TextMeshPro 8,635 ms and UI Toolkit 6,446 ms of synchronous main-thread work — a 263× / 196× reduction in main-thread cost (per-glyph: 10.8 µs vs 2,793.6 / 2,084.8 µs). The atlas finishes filling ≈ 2.1 s later without ever blocking a frame. A decorative 609-glyph RubikStorm fill: 96 ms vs 2,025 ms (TMP) / 826 ms (UI Toolkit).
- AddedBinned GPU rasterizer (
UniTextGlyphRaster.compute+GpuRasterPrep): CPU-precomputed 2D binning — 16 row-bands and a 16×16 control-hull cell grid — so each GPU texel evaluates only the local candidate segments instead of the glyph's whole outline. Heavy fonts (CJK, decorative, complex Arabic) rasterize far faster than v2's single 1D scan. - AddedBurst contour resolution (
ContourUnionBurst): a new zero-allocation[BurstCompile]stage on raw pointers resolves self-intersecting and stroked outlines up front; cleanly-resolved glyphs are tagged so the SDF/MSDF sampler skips the per-pixel silhouette heuristic 2.x ran for every glyph. - AddedParallel CPU rasterization (
GlyphBatchRasterizer+ per-font worker fan-out): a frame's glyph requests batch per font; fonts render concurrently, and within one font outline extraction and Burst SDF generation spread across every core — ≈ 9.7× over single-threaded on a 12-core desktop (3,029 NotoSans glyphs: 220 ms vs 2,138 ms; Editor measurement of the CPU-raster path). Platforms without compute support (WebGL) take this path automatically. - AddedGPU raster robustness:
GraphicsFence-tracked completion polled at frame granularity, a submission-cost budget (250 ms mobile / 500 ms desktop), and aDeviceResetSentinelthat re-arms the atlas after a graphics-device loss or scene-reload — text survives alt-tab/driver resets without corruption. - AddedAtlas growth tapers: past 16 slices the glyph atlas grows +25 % per step instead of doubling, and pre-allocation discounts reusable page area — large multilingual atlases stop overshooting their memory footprint, and a double-allocation crash risk on the CPU-raster path with very large glyph sets is gone.
- AddedBurst-compiled analysis with a byte-identical fallback: the four Unicode analysis passes — UAX #9 BiDi, #24 script, #29 grapheme, #14 line-break — each run as a single
[BurstCompile], auto-vectorized kernel exposed as aFunctionPointer, with the exact same source available as managed IL where Burst is unavailable. Output is bit-for-bit identical either way (integer-only logic) — speed with zero behavioral risk. (These are Burst auto-vectorized kernels, not hand-written SIMD intrinsics.) - AddedPer-paragraph, parallel, cached analysis: because the kernels are
FunctionPointers (not UnityIJobs) they run on theWorkerPoolthreads; analysis is paragraph-scoped, and a content-addressedParagraphAnalysisCache(XxHash64) reuses resolved levels/scripts/line-break and grapheme boundaries so editing one line never re-analyzes the document. In 2.x each document was analyzed whole, on one thread, uncached. - AddedUnchanged paragraphs are never re-shaped (
ParagraphShapeCache): each paragraph is fingerprinted over its codepoints plus everything that feeds shaping (font size, per-range font/language data, hidden markup), and a hit replays the stored pristine HarfBuzz runs and glyphs — the Blink shape-cache model. Typing re-shapes only the edited paragraph. Android release build, 100 multilingual objects: an incremental edit rebuilds in 43 ms vs 69 ms for the all-new cold path. - AddedCross-document word-shape cache (
WordShapeCache): shaped words are reused across every text on the engine (XxHash64-keyed by codepoints, font, variation axes, script, direction, language) — changing labels, counters, and chat logs that share vocabulary splice stored glyphs with no HarfBuzz call at all. Only words HarfBuzz marks safe-to-break on both boundaries are cached — the exact per-font condition under which the isolated word shapes identically to its in-context form — and entries hold font-unit glyphs, so they survive font-size changes; a miss shapes the whole run in context, byte-identical to the uncached path. - AddedOne document parallelizes too: the parallel pipeline (on by default since 2.x) fanned out per component; analysis, shaping, and layout are now paragraph-scoped, so one large text also spreads its bidi/script/break/shaping work across every core — per-paragraph shape jobs from every dirty component flatten into a single worker-pool run. Release build (Galaxy S21 FE 5G, Vulkan, IL2CPP), cold rebuild of 100 multilingual objects: UniText 69 ms vs UI Toolkit 167 ms — both HarfBuzz-shaped, like-for-like — and TextMeshPro 455 ms (TMP does not shape Arabic/bidi; its output is not equivalent). Editor, 12-core Ryzen 9 5900X: 92 ms parallel vs 351 ms single-threaded (≈ 3.8×).
- AddedViewport-windowed rendering (
UniTextBase.VisibleWindow): a local-space window bounds mesh emission — paragraphs fully outside it produce no quads, while layout, selection, caret, and hit-testing stay whole-document. Canvas text feeds it automatically from the enclosingRectMask2Dclip rect, so a long document in an input field or ScrollRect only meshes the visible band (a half-window hysteresis margin makes small scrolls free); set it explicitly for custom virtualized scrollers (chat, logs) —nullrenders everything. - AddedPure-LTR BiDi fast path: all-LTR text (Latin, Cyrillic, CJK — the common case) short-circuits the full X1–L1 bidirectional cascade after one classification scan, a fast path 2.x had no equivalent of.
- AddedNative-memory Unicode tables: the whole Unicode character database moved into
NativeArray<T>(Persistent) — off the GC heap, shared by both the Burst and fallback paths. - AddedTrigger-scan markup parsing: every
ParseRuledeclares the characters that can start its match, and the parser bakes their union into an ASCII jump table — the match walk skips runs of plain text without probing any rule, degrading to the 2.x linear walk for exotic trigger sets. - AddedSingle source per algorithm: each UAX rule now exists in exactly one place, collapsing the engine classes to thin wrappers (BidiEngine 2,287 → 402 lines, LineBreakAlgorithm 680 → 122) — less surface to break, zero drift between fast path and fallback.
- AddedUnified coverage × paint SDF pipeline: every glyph quad picks a
CoverageMode(Fill/Stroke/Shadow/InnerShadow) and aPaintKind(Solid/Gradient/Texture); plain glyphs leave both at 0 and pay nothing, while styled text composes Photoshop-style layer effects on the GPU with one draw call per material. Backed byUniText_Coverage.hlsl+UniText_Paint.hlsland shared decode includes (UniText_AtlasDecode.hlsl,UniText_SdfSample.hlsl) that de-duplicate four hand-copied decode paths. - AddedLit world text is a checkbox (
UniTextWorld.Lit): one per-component toggle renders the text with scene lighting — the lit SDF/emoji materials are selected automatically, and adjacent instances can independently be lit or unlit. In 2.x this required hand-assigning the shipped lit materials through a custom-material style.CastShadows(independent ofLit, off by default) opts the batched renderer into shadow casting, resolved per sorting context — 2.x lit text always cast, with no supported way to turn it off. - AddedUnlit fast path on the Lit shaders:
_LightInfluence0 branches around the whole lighting computation (UNITY_BRANCHon a uniform — no extra keyword or variant), instead of 2.x's compute-then-lerp — a world-text material dialed to unlit pays no per-fragment lighting cost. - AddedWorld shaders ship via the settings asset:
UniTextSettingsnow retains the world-spaceUniText/Lit/SDF/UniText/Lit/Emojiand CanvasUniText/UI/Highlightshaders (six required-shader slots, up from three), so world text and highlight materials resolve in builds without touching Always Included Shaders; the new Include Lit Shaders toggle (Project Settings → UniText → Rendering, default on) drops the Lit pair from builds that use no lit text. - AddedShared gradient-ramp atlas (
GradientRampAtlas): one 256-wide row per unique gradient, deduped by content and ref-counted, uploaded row-granularly — crisp gradients that stay allocation-free and animate at one texture-row upload per frame, shared across all text and highlights on the device. - AddedRounded, gradient highlight shaders: the world
UniText/Highlightgained a rounded-rect SDF with gradient-ramp paint, and a new CanvasUniText/UI/Highlightprovides the uGUI counterpart — rounded, gradient-filled selection/find highlights on both backends. - Added
UniTextAnimationBridge: a sibling component that makes UnityAnimator-driven changes update the text — add it next to aUniText/UniTextWorldand add the handlers for the fields and modifiers you animate; text without the component does no per-frame diff work. - AddedComponent field handlers —
UniTextFieldsAnimationHandler(font size, colour, word wrap, auto-size with min/max, alignment) andUniTextWorldFieldsAnimationHandler(the same, plus sorting order and layer). - AddedModifier handlers —
ColorAnimationHandler,SizeAnimationHandler,LetterSpacingAnimationHandler,GlowAnimationHandler,ShadowAnimationHandler,StrokeAnimationHandler, andPhaseAnimationHandler(keyframe a phase-driven glyph animation — binds the host's firstIPhaseDrivenmodifier), each with the correct per-parameter invalidation. - Added
AnimationHandler/ModifierAnimationHandler:[SerializeReference]extension points — subclass to animate a custom component's fields or a custom modifier's[Parameter]fields from anAnimator. - AddedInline Scene-view text editing: double-click any
UniTextBasein the Scene, press F2, runTools/UniText/Edit Text in Scene, or click the inspector's "✎ Edit in Scene View" button to open a live editing session — caret, selection, drag-select, word/paragraph click, keyboard nav, clipboard, undo/redo, and OS IME (candidate window anchored to the Scene-view caret) — all driven by the runtime engine, with a floating formatting overlay (B/I/U, colour, markup visibility) and a right-click menu. On exit the edited markup is written back throughUndo.RecordObject, and every helper component the session spawned is removed, leaving the GameObject graph exactly as it was. - AddedAutomatic v2.x → 3.0 asset migrations: nine concrete
IMigrationclasses upgrade serialized scenes/prefabs/assets on first load (byte-preserving YAML splices, auto-run on version change, no prompts, CI excluded) —Outline→Stroke,Gradient→Fill,Wobble→Wave, the gradient-provider trio →InlinePaintProvider/AssetPaintProvider/GlobalSettingsPaintProvider, a bold-weight sentinel fix,DefaultTextHighlighter→StateHighlightStyler, and legacyUniTextGradientscatalogs →UniTextPaints. A breaking major upgrade that costs the user nothing. - AddedCustom inspectors for
UniTextInputField,UniTextEditable,UniTextSelectable, andInputBehaviorPreset, backed by the shared[SerializeReference]type picker with grouped, icon-labelled, description-carrying "Add" menus for behaviors, decorators, filters/validators, and context-menu items.InputBehaviorPresetEditoraudits preset assets for behaviors pointing at scene objects. - AddedNew property drawers:
PaintSwatchDrawer,UnitValueDrawer(value + unit dropdown),InheritableFieldDrawer(NaN inherit sentinel),ChromeRuleDrawer,FormatCommandDrawer, andModifierBodyDrawer(shows a modifier's[Parameter]fields as an opt-in override list — a field appears only when it differs from the default, and edits route through the live C# property for scoped invalidation). - AddedClipboard Inspector window (
Tools/UniText/Clipboard Inspector): shows every format a copy wrote — plain text, UniText source, HTML, Markdown, URL, custom MIME, images (with a decoded preview) and files — with counts, decoded payload, and whitespace visualization. - Added
FocusGuard(in LightSide.Core): marks a UI hierarchy (formatting toolbar, context panel) focus-preserving, so pressing its controls neither defocuses the editor nor drops the selection — the toolbar contract of a word processor. One drop-in component;FocusGuard.PointerIsOverGuarded()recovers the press target. - Added
UniTextFocusable: a hidden, runtime-managedSelectablethat makes an editable surface a Tab stop (Navigation.Mode.Automatic) while a selection-only surface stays pointer-focusable but Tab-excluded (the web model) — reconciled automatically, never added by hand. - AddedAccessibility preferences:
Accessibility.PrefersReducedMotion(LightSide.Core, with a change event; honored byFloatingLabelDecoratorand any driver that consults it — maps to iOS/macOS Reduce Motion, Android Remove Animations, webprefers-reduced-motion) andInputCaretRenderer.PrefersNonBlinkingCaret(suppresses caret blink — iOS "Prefer Non-Blinking Cursor"). The app sets the flags from platform prefs. - AddedOne-click scene scaffolding:
GameObject → UI (Canvas) → UniText → Selectable Text / Editable Text / Input Fieldinstantiate the project's own prefab fromUniTextSettings.{SelectableText,EditableText,InputField}Prefab(the Unity way — a designer's configured prefab, not a code-built hierarchy), provisioning a Canvas + EventSystem (wiringInputSystemUIInputModuleunder the new Input System) and warning when a slot is empty. - AddedShipped
Defaults/assets:Selectable Text,Editable Text,Input Fieldprefabs plus the three selection-handle sprites for the touch UI. - AddedNew
EditableTextsample: a complete working editor scene — the selectable → editable → input-field ladder, anInputBehaviorPreset, a password field with a reveal toggle, a caret-context formatting toolbar, and chat-style media paste turning pasted images into attachment cards. - AddedExpanded Basic Usage sample: auto-detected
@mention/#hashtagchips, tap-to-reveal spoilers, a live-swappable customRangeStyler, Find-in-Page overFindAllpainted onto highlight layers, and aLanguageFonts/set covering 17 non-Latin scripts. TheModifierssample is renamedStyles;FontExamplesgains a variable-fontEdgeCasesFonts/set (Comfortaa and Outfit variable-weight fonts). - AddedCI: a GitHub Actions
urp-compile-checkmatrix imports the package into URP 12.1 / 14.0 / 17.0 projects and fails on any shader import error, and three workflows build the Windows/macOS/Android native input plugins reproducibly from committed source; LightSide.Core adds its own workflows for the GPU-upload and profiler-sampler plugins — shaders proven across three URP majors, and native binaries transparent, not opaque blobs. - AddedOptional Input System: documented as an optional dependency (
UNITEXT_INPUTSYSTEMviaversionDefines) — works with the old Input Manager, the new Input System, or both. Native text input works regardless either way.
- ChangedUniText now depends on
media.lightside.core1.0.0 and is layered on top of the shared foundation extracted into that package (see Two packages). Source-compatible apart from two utility renames (UniTextArrayPool<T>→ArrayPool<T>,RangeEx.WholeText/IsWholeText→RangeEx.All/IsAll); the moved types keptnamespace LightSide. - Changed
IParseRuleinterface replaced by theParseRuleabstract class (breaking, no alias). Custom parse rules now subclassParseRuleand overrideTryMatch; the base carries the syntax-introspection contract (Identity,SourceToken,MarkupTriggers/ScanTriggers,CanWrap,Apply) that lets the editor apply/toggle/round-trip a style in its own markup. - ChangedStyleCore gained editor-grade introspection:
ParseRulenow describes and re-emits its own syntax (all public — see Rich-text formatting), andUniTextBasegainedEnsureStyleForandGetModifier(Type)— the surface that powers the formatting toolbar. Style addressing is by modifier signature (a concrete exemplar, resolved internally); there is no name-based style lookup. - ChangedPipeline events are per-component now: new
UniTextBaseinstance events —FrameUpdated(once per frame while the component updates; the tick range stylers and modifier animations subscribe to) andLayoutCommitted(fires only when this component reprocessed and its glyph geometry is final — the moment to rebuild coordinate maps, caret geometry, or overlay positioning) — replace the removed public staticUniTextBase.BeforeProcess/MeshApplied/AfterProcess(breaking, no alias). A subscriber now pays only for the component it watches, never for every text update in the scene. - Changed
InteractiveModifierwent from an abstract, subclass-required handler to a concrete, inspector-pickable modifier with a per-range state machine, six range events, and aRangeStylerseam (see Highlight layers, find-in-page & interactive ranges). Range pointer events moved offUniTextBaseonto the modifier. - ChangedPointer/click events on
UniTextBaseuse theTextPointerEventtype.TextClickednow carries a consumableTextPointerEvent(device kind, modifiers, trigger, consume flag) instead of a bareTextHitResult; the previous base range pointer events (RangeClicked/RangeEntered/RangeExited,IsHoveringRange/CurrentHoverRange) were removed in favor ofInteractiveModifier's events. - Changed
UniTextBase.HitTest/HitTestScreenrenamed toHitTestRange(range/bounding-box "which entity is here" semantics), distinct from the new caret hit-testing (HitTestCaret). - ChangedStyle modifiers rebuilt around the paint system: the whole visual half of StyleCore now runs on one paint abstraction (fills/strokes/shadows/glows/decorations take any solid/gradient/texture paint), every parameter gained a serialized default + public property + per-range override, and files reorganized into
TextStyle/Layout/Appearance/Utility/Decoration/Effects/Mediasubfolders (see Text effects & paint). - ChangedHighlight rendering moved from a flat single-color rect list to named, prioritized highlight layers: the
TextHighlighter/DefaultTextHighlighterfamily and the serialized highlighter field onUniTextBasewere removed — selection visuals viaUniTextSelectable.SelectionStyle, interactive-range feedback viaRangeStyler, everything else throughUniTextHighlights. - ChangedThe world batcher groups by sorting context, not material: a batch is keyed by (sorting layer, order,
SortingGroup, GameObject layer, scene) with materials demoted to sub-meshes, so one component's cross-material layers — emoji beside SDF glyphs, texture-paint fills, per-style custom materials — draw in exact Styles order, the way Canvas sibling order does (in 2.x each material was its own batch mesh, with unspecified relative order). Inside a context, entries pack into size-bounded shards (target raised 8,192 → 16,384 vertices, tunable viaUniTextSettings.WorldBatcherShardTargetVertexCount) so a structural change re-bakes only the owning shard; index buffers escalate UInt16 → UInt32 automatically past 65,535 vertices. - Changed
UniTextDirtyFlagsrenamed toUniTextDirty(breaking, no alias) and re-cut to name pipeline stages instead of properties:Color→Mesh,Alignment→Positions,FontSizefolded into the existingLayout;Text/Font/Direction/Materialkeep their names;Sortingand theLayoutRebuildcomposite were removed (sorting propagates through events, not a rebuild flag). Recompile against the new enum; do not persist raw values. - ChangedText animation is now phase-driven, not self-animating: v2's
WobbleAnimationModifier(readTime.time, rebuilt every frame) is renamedWaveModifier(auto-migrated,speed→frequency) and re-cut as a pure function ofPhase— controllable and scrubbable instead of a fire-and-forget frame-eater (see Kinetic text animation). - ChangedAnimator support is opt-in via
UniTextAnimationBridge: animating a component's own fields now requires the bridge with the matching field handler, instead of every Animator-driven component diffing its fields automatically. - ChangedThe GPU glyph-upload backend was rebuilt (ABI v3) and moved to LightSide.Core as the public
GpuUploadAPI, with device-loss and multi-backend (D3D/Metal/Vulkan/WebGL2) handling. - Changed3D text shadows match the visual: the shadow casters now cast each visible layer's coverage, so an outlined or glowing glyph throws a shadow shaped like what you see, not the bare core glyph.
- RemovedLegacy gradient & outline modifiers:
GradientModifier,OutlineModifier,IGradientProvider/IHasGradientProvider, and theInline/Asset/GlobalSettingsgradient-provider family — superseded by gradient paints on any layer viaIPaintProviderand byStrokeModifier— along with theUniTextGradientscatalog asset andUniTextSettings.Gradients(superseded byUniTextPaintsandUniTextSettings.Paints; a migration converts each legacy catalog into a-Paintssibling asset). The<gradient>/<outline>tags survive as deprecated parse rules so existing markup still renders; the removed types have no alias (asset data auto-migrates). - Removed
TextHighlighter/DefaultTextHighlighterand the serializedHighlighterfield onUniTextBase— replaced by highlight layers,SelectionStyle, andRangeStyler. - RemovedInteractive-range dispatch types
InteractiveRangeRegistry,IInteractiveRangeProvider,IInteractiveRangeHandler, andInteractiveRange.priority—InteractiveModifiernow self-subscribes to the pointer surface; overlapping ranges from different modifiers all dispatch, in registration order. - RemovedStatic pipeline events
UniTextBase.BeforeProcess/MeshApplied/AfterProcess(breaking, no alias) — replaced by the per-componentFrameUpdated/LayoutCommitted(see Changed). - Removed
AnimationHandlerBase<T>: extending the Animator diff is now done by adding a field/modifier handler to aUniTextAnimationBridge.
