fix(ui): round-5 review polish — S1 block outline pass, S2 non-UiText outline paths, S3 citation fix

Collects the post-gate polish left uncommitted by the killed round-5 agent
(S1/S3 + review fixes N1/N3/N4) and completes the missing S2 half:

- S1: UiText multi-line transcript + colored-run label now submit EVERY
  line/run's outline pass before ANY fill pass, matching retail's
  UIElement_Text::DrawSelf @0x00467aa0 whole-block walk. DrawStringDatPass
  is exposed for block-level batching; single lines keep DrawStringDat.
- S2 (completed this commit): authored outline 0x21/0x22 now reaches every
  text-bearing widget — UiButton, UiDatElement, UiField, UiMeter, UiMenu,
  UiCatalogSlot — seeded from the element's effective-default state exactly
  like UiText (BuildButton lifts the label-bearing Text child's authored
  value first, same chain as the label color). Per-STATE outline switching
  (dialog/character/combat buttons author 0x21 in state 0x3 only) is NOT
  ported — filed as register row AP-192 in this commit.
- S3: ChatWindowController reconciliation comment corrects the misread
  indicator action ids 0x10000514-17 -> 0x10000114-17 and re-attributes the
  id-coincidence to the pagination widget's m_prevButton/m_nextButton, not
  gmFriendsUI; register + window-shell research doc corrected to match.
- N1: LayoutImporter's duplicate per-state any-state-first-wins 0x21 read is
  deleted — ElementReader.ApplyCanonicalLegacyProjection's DirectState-then-
  effective-default resolution is the single source (the duplicate would have
  lit state-0x3-only outlines permanently once S2 widened consumption).
- N3: the outline pass tints with the outline color's OWN alpha, not the
  fill's (retail tints m_curOutlineColor and m_curTextColor independently).
- N4: the outline-inflated glyph SOURCE rect is clamped to the atlas bounds
  with matching dest shrink, porting CreateCharRectPair @0x00441480's edge
  behavior — edge glyphs crop instead of sampling a neighbour's texels.

Full Release suite: 12,610 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-10 22:15:20 +02:00
parent ed0dbff90a
commit aa6635aebf
14 changed files with 258 additions and 66 deletions

View file

@ -248,7 +248,9 @@ fixtures:**
DO author an Enum-kind property `0x12` (confirmed by `Kind: 0` = Enum in the
fixture's own property dump, matching `LayoutImporter.ConvertProperty`'s
`EnumBaseProperty → UiPropertyKind.Enum` mapping exactly), with values
`0x10000514`-`0x10000517` in element-id order. This is a real, present,
`0x10000114`-`0x10000117` in element-id order (re-verified directly against
the committed fixture's own `UnsignedValue` fields — an earlier pass here
misread these as `0x10000514`-`0x10000517`). This is a real, present,
correctly-typed action id — the button-click half of the generic mechanism
is genuinely armed.
- `chat_floaty_2100005b.json` — the floating chat window's own fixture
@ -262,14 +264,19 @@ fixtures:**
`DoVisibilityToggleAction` would look up the button's action id, find zero
registered listeners, and silently return — the click would fire a real
message with no receiver.
- The four action-id VALUES (`0x10000514`-`0x10000517`) are also not
chat-specific: the identical four numbers, in the identical order, are
`gmFriendsUI::PostInit`'s own child-element ids for its Add/Remove/Tell
buttons and friends listbox (`UIElement::GetChildRecursive(this,
0x10000514)` etc., confirmed by direct read). This is almost certainly a
- The four action-id VALUES (`0x10000114`-`0x10000117`) are also not
chat-specific: the first two, `0x10000114` and `0x10000115`, are
`m_prevButton`/`m_nextButton` child-element ids for an UNRELATED
pagination widget elsewhere in the decomp (`UIElement::GetChildRecursive(this,
0x10000115)` / `(this, 0x10000114)`,
`acclient_2013_pseudo_c.txt:194343-194344`, confirmed by direct read) — a
coincidence of Turbine's global per-dat-file asset-id allocator (ids are
assigned client-wide, not scoped per panel), not a cross-reference —
nothing in `gmFriendsUI` registers for input actions either.
assigned client-wide, not scoped per panel), not a cross-reference.
(**Correction:** an earlier pass here misread the fixture's Enum values as
`0x10000514`-`0x10000517` and built a since-retracted claim that they
matched `gmFriendsUI::PostInit`'s own Add/Remove/Tell child ids — that
match does not exist at the correct `0x1000011x` values and `gmFriendsUI`
is not part of this finding.)
**Conclusion: the generic UI action system is real, it exists, and the
buttons genuinely arm their half of it — but the authored DATA available to