fix(chat): CH6a/b rework — grip media, retail window-id model, floaty fixture

Applies docs/research/2026-08-10-ch6ab-review-findings.md in full:

- BLOCKER 1: UiResizeGrip now carries its ElementInfo/resolve pair and
  draws its own authored DirectState media (a synthetic parameterless
  grip still draws nothing, preserving existing resize-drag tests).
  DatWidgetFactory.BuildResizeGrip threads resolve through. All seven
  live grips on the main chat window now resolve a non-zero sprite,
  restoring the visible borders/corners CH6a silently dropped.

- SHOULD-FIX 2: ChatWindowState gains BroadcastTargetWindow, a sentinel
  distinct from every real window id (0-4), fixing the bug where the
  main window's explicit-addressing branch coincided with the broadcast
  check (both were literal 0). SetFilter's main-window no-op is dropped
  — the main window's filter is now genuinely settable. ChatWindowController
  .Bind takes a ChatWindowState (the same canonical instance the floating
  windows already share) and GetTranscriptLines builds a real accept
  predicate instead of accept:null. Verified safe: ClientLocal (0x1A)
  never reaches ChatLog (AddText routes it to the SpewBox and returns),
  so nothing observable regresses.

- SHOULD-FIX 3: UiButton.SuppressSelfToggle stops the four chat-window
  indicator buttons (DAT property 0x0B=true, no retail click handler)
  from flipping their own Selected mirror on a stray click.

- SHOULD-FIX 4: generated and committed chat_floaty_2100005b.json from
  the real installed dats; added the permanent RetailLayoutFixtureGenerator
  entry. All three flagged FloatingChatWindowController assumptions
  (input field, title bar, close button) are confirmed correct against
  real data — no controller code changes needed. New finding: unlike the
  main window, ALL EIGHT floaty border/corner elements are live Type-9
  grips (the floaty's own title bar is its move handle), so a floaty
  window resizes from every edge and corner.

- SHOULD-FIX 5: register row AP-189 documents the shared-500-entry/
  200-line-tail vs retail's per-window 10,000-line scrollback depth gap.

- NITs 1-5: documented the filter-persistence-only-on-/saveautoui
  asymmetry and the reconnect-preserves-filters intent; corrected the
  research doc's modifier-mask mislabel and the "ONLY function" false
  superlative; moved WrapText off ChatWindowController onto
  ChatTranscriptRenderer, closing the circular dependency.

Full Release suite: 12,420 passed / 4 skipped / 0 failed (baseline
12,392/4/0 at 22020ef2; net +28 tests, zero regressions).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-10 13:09:32 +02:00
parent 56b84deeab
commit 1aa7709988
23 changed files with 6284 additions and 248 deletions

View file

@ -87,11 +87,30 @@ ToggleFloatingChatWindow4 [ "" [ 0 DIK_4 ] 0x00000004 ]
```
`0x00000004` is the modifier mask (the same mask the `UseQuickSlot_14..18` rows
use for `Alt`+digit, versus `0x00000002` for the shift-digit quickslot rows).
use for `Alt`+digit, versus `0x00000002` for the CTRL-digit quickslot rows —
corrected below; the original filing mislabeled `0x00000002` as "shift").
So retail's default is **Alt+1 … Alt+4**, not bare 14.
UNVERIFIED: the exact modifier bit → key mapping (4 = Alt vs 4 = Ctrl). Cheapest
resolution: read `keymap-default.txt`'s modifier legend, or one cdb breakpoint on
`UIElementManager::KeyPressEvent`. The *mechanism* below is independent of this.
RESOLVED at Campaign CH slice CH6b: `retail-default.keymap.txt`'s own
`MetaKeys` legend (not a guess — the keymap file's literal index table) reads
```
MetaKeys
[
1 [ 0 DIK_LSHIFT ]
2 [ 0 DIK_LCONTROL ]
2 [ 0 DIK_RCONTROL ]
3 [ 0 DIK_LMENU ]
3 [ 0 DIK_RALT ]
4 [ 0 DIK_LWIN ]
4 [ 0 DIK_RWIN ]
]
```
so the modifier-mask bit for MetaKeys index *N* is `1 << (N-1)`: **index 1 =
Shift → `0x1`, index 2 = Ctrl → `0x2`, index 3 = Alt → `0x4`, index 4 = Win →
`0x8`**. `0x00000004` on the `ToggleFloatingChatWindow1..4` rows is therefore
unambiguously **Alt**, cross-checked against the same file's own Alt+A/D
strafe and Alt+Enter/Tab/F4 rows (all `0x00000004`). `KeyBindings` already
carried `ModifierMask.Alt` for these four actions since Phase K.1c — no code
change was needed, only removing this hedge.
The dispatch chain is fully generic — there is no chat-specific code in it:
@ -151,10 +170,15 @@ State 6 = "on/depressed", state 1 = "normal". No handler anywhere in the binary
switches on `0x10000522..0x10000525` as a *source* of a click — `grep` over the
whole pseudo-C returns only `gmMainChatUI::RecvNotice_SetPanelVisibility`.
**RESOLVED 2026-08-10 at Campaign CH slice CH6b.** The prior UNVERIFIED
paragraph's hedge ("safe to wire both") is superseded by a direct read of
`gmMainChatUI::ListenToElementMessage @0x004CDA80` — the ONLY function in the
whole 2013 binary that branches on `idMessage == 1` ("clicked"). It handles
**RESOLVED 2026-08-10 at Campaign CH slice CH6b; wording corrected at the
CH6a/b REJECT-review (NIT 4) — the original "ONLY function in the whole 2013
binary that branches on `idMessage == 1`" superlative was false**
(`gmFloatyChatUI::ListenToElementMessage @0x004CE330`, the floaty windows'
close-button handler, also branches on `idMessage == 1`; §1.5). **The
substantive claim stands: within `gmMainChatUI::ListenToElementMessage`
specifically, there is no case for `0x10000522`-`0x10000525`.** The prior
UNVERIFIED paragraph's hedge ("safe to wire both") is superseded by a direct
read of `gmMainChatUI::ListenToElementMessage @0x004CDA80`. It handles
exactly two element ids: `0x1000046f` (max/min, dispatching
`HandleMaximizeButton`) and the talk-focus menu's selection message
(`idMessage == 7`, checked against `this->m_pCCS` / a `0x1000000b` attribute