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:
parent
56b84deeab
commit
1aa7709988
23 changed files with 6284 additions and 248 deletions
|
|
@ -116,7 +116,8 @@ public sealed class ChatWindowStateTests
|
|||
{
|
||||
var state = new ChatWindowState();
|
||||
// Addressed to window 2, evaluated from window 1's perspective: not a
|
||||
// broadcast (targetWindowId != 0) and not addressed to window 1.
|
||||
// broadcast (targetWindowId is a real window id, not
|
||||
// BroadcastTargetWindow) and not addressed to window 1.
|
||||
Assert.False(state.ShouldDisplay(windowId: 1, targetWindowId: 2u, logTextType: 0x02u));
|
||||
}
|
||||
|
||||
|
|
@ -124,38 +125,65 @@ public sealed class ChatWindowStateTests
|
|||
public void ShouldDisplay_Broadcast_FilterHit_Shows()
|
||||
{
|
||||
var state = new ChatWindowState();
|
||||
Assert.True(state.ShouldDisplay(windowId: 1, targetWindowId: 0u, logTextType: 0x02u)); // Speech
|
||||
Assert.True(state.ShouldDisplay(
|
||||
windowId: 1, targetWindowId: ChatWindowState.BroadcastTargetWindow, logTextType: 0x02u)); // Speech
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDisplay_Broadcast_FilterMiss_DoesNotShow()
|
||||
{
|
||||
var state = new ChatWindowState();
|
||||
Assert.False(state.ShouldDisplay(windowId: 1, targetWindowId: 0u, logTextType: 0x0Au)); // Social
|
||||
Assert.False(state.ShouldDisplay(
|
||||
windowId: 1, targetWindowId: ChatWindowState.BroadcastTargetWindow, logTextType: 0x0Au)); // Social
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDisplay_MainWindow_ShowsEveryBroadcastRegardlessOfSeededFilter()
|
||||
public void ShouldDisplay_MainWindow_BroadcastRespectsItsOwnFilter()
|
||||
{
|
||||
// CH6a/b REJECT-review BLOCKER (SHOULD-FIX 2): before the fix, window
|
||||
// 0's explicit-addressing branch (targetWindowId == windowId) used the
|
||||
// SAME literal 0 as the broadcast sentinel, so it short-circuited true
|
||||
// for EVERY broadcast line regardless of window 0's own filter. Now
|
||||
// that BroadcastTargetWindow is a distinct sentinel, the main
|
||||
// window's filter (0xFBFFFFFF — excludes 0x1A, Society opt-in) is
|
||||
// genuinely consulted for a broadcast line, exactly like every other
|
||||
// window.
|
||||
var state = new ChatWindowState();
|
||||
Assert.False(state.ShouldDisplay(
|
||||
windowId: 0, targetWindowId: ChatWindowState.BroadcastTargetWindow, logTextType: 0x1Au)); // excluded
|
||||
Assert.False(state.ShouldDisplay(
|
||||
windowId: 0, targetWindowId: ChatWindowState.BroadcastTargetWindow, logTextType: 0x20u)); // Society, opt-in
|
||||
Assert.True(state.ShouldDisplay(
|
||||
windowId: 0, targetWindowId: ChatWindowState.BroadcastTargetWindow, logTextType: 0x02u)); // Speech
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDisplay_MainWindow_ExplicitlyAddressed_AlwaysShows()
|
||||
{
|
||||
// Window id 0 (main) is still a valid EXPLICIT address, distinct from
|
||||
// BroadcastTargetWindow — this is what keeps a future
|
||||
// targetWindowId == MainWindowId (AP-180's per-window echo)
|
||||
// expressible: explicit addressing to main always wins, even for a
|
||||
// type main's own filter would otherwise reject.
|
||||
var state = new ChatWindowState();
|
||||
// Window 0's own filter is 0xFBFFFFFF (excludes 0x1A) but that filter
|
||||
// is never actually consulted for a broadcast message: targetWindowId
|
||||
// (0) == windowId (0) is already true via the first branch.
|
||||
Assert.True(state.ShouldDisplay(windowId: 0, targetWindowId: 0u, logTextType: 0x1Au));
|
||||
Assert.True(state.ShouldDisplay(windowId: 0, targetWindowId: 0u, logTextType: 0x21u));
|
||||
}
|
||||
|
||||
// ── SetFilter / SetOpen / Toggle ─────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void SetFilter_MainWindow_IsANoOp()
|
||||
public void SetFilter_MainWindow_Persists()
|
||||
{
|
||||
// CH6a/b REJECT-review SHOULD-FIX 2: dropped the old no-op — the main
|
||||
// window's filter is now genuinely settable, matching retail's own
|
||||
// options-page-driven main-window filter.
|
||||
var state = new ChatWindowState();
|
||||
ulong before = state.GetFilter(0);
|
||||
|
||||
state.SetFilter(0, 0u);
|
||||
state.SetFilter(0, 0x1u);
|
||||
|
||||
Assert.Equal(before, state.GetFilter(0));
|
||||
Assert.Equal(0x1u, state.GetFilter(0));
|
||||
Assert.True(state.TypeIsActive(0, 0x00u));
|
||||
Assert.False(state.TypeIsActive(0, 0x02u));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue