Vixen
dd8b0a81
csharp
public sealed class TransformedText

An element's text after text-transform, and the map back to what was written.

Read the guide page for this →

Remarks

The map is the point, and the four keywords are the easy part. A full Unicode case mapping changes the UTF-16 length — straße uppercases to STRASSE, one character becoming two — and what a text layout hands out is every one of them an index: a caret index, a selection range, the start of a line. Shipping the keywords without this puts the caret in the wrong character of an editable field, silently, and only on the strings where it expands.

⚠ .NET's own casing would never have shown that. string.ToUpperInvariant, ToUpper in every culture, and Rune.ToUpperInvariant over all 1 112 064 scalars implement the simple mappings, which are one code point to one by definition — measured, not assumed. So the naive implementation is index-safe and draws STRAßE, which is a different defect and a visible one. The expansions come from SpecialCasingTable, which is the UCD's unconditional rows and is what makes this type necessary at all.

⚠ ToSource is many-to-one and ToDrawn is not. Both units of the SS that came from one ß map back to that ß, because there is no caret position between them — the two letters are one character the author typed. So source → drawn → source is the identity and drawn → source → drawn is not, and a click in the middle of an expansion snaps to its start. That is the behaviour a text field wants and it is the reason the pair is not a single offset.

Identity is the overwhelmingly common case — no transform at all, or a transform under which every character keeps its length — and it allocates no arrays and returns the same string instance, which is what keeps the shaping cache's fast path and UiElement.Block's reference test meaning what they meant.

Fields and properties (3)

  • public string Source

    The text as it was written.

  • public string Text

    The text as it is shaped and drawn.

  • public bool IsIdentity

    Whether every index means the same thing in both strings.

Methods (3)

  • public static TransformedText Of(string? source, TextTransform transform, string? language = null)

    Applies a transform, and builds the map if it moved anything.

  • public int ToDrawn(int index)

    Where a source index sits in Text.

  • public int ToSource(int index)

    Which source index a drawn index belongs to.

Used by (6)

  • FinalSigmaTestsVixen.Ui.Text.Tests
  • LithuanianCasingTestsVixen.Ui.Text.Tests
  • TextLineVixen.Ui
  • TransformedTextTestsVixen.Ui.Text.Tests
  • TurkicCasingTestsVixen.Ui.Text.Tests
  • UiElementVixen.Ui