fix(chat): round 4 — no user-visible meta text, real /help groups, indicator buttons toggle
Item 3 (#364): every honesty marker is now gone from user-visible /help text. AllegianceOverview/HouseOverview's "[IMPLEMENTED]" tags and trailing "Subcommands NOT marked..." sentences, and Day/Log/Render/Motd's appended "NOT YET IMPLEMENTED in acdream" tails, are removed; the underlying retail text is corrected/completed against the pseudo-C's own pristine consolidated data dumps (Log and Motd had been silently truncated; Render was entirely acdream-authored and is replaced with the real retail usage string). The three PARTIAL /help group topics (channels/chatting/commands) are now COMPLETE verbatim listings: HelpStupidChannelHack's three "vtable slot" operands, previously believed undecodable, are the same pooled/mislabeled-data artifact this campaign has hit before (AP-113's precedent) — reading the function's own disassembly for the push imm32 preceding each constructor call resolves all three directly. messagetypes is now a real ported construction (IsLegalChannel's 14-id whitelist + LogTextTypeToString's name table + the exact join/wrap format) instead of an acdream summary. Register row AP-184 retired. Item 5: the main window's 1/2/3/4 indicator buttons now toggle their floating chat window on click, per the user's retail memory overruling the earlier decomp-only reading. UIElement_Button::HandleButtonClick has its own generic click-driven action dispatch (property 0x12) reaching the same DoVisibilityToggleAction the Alt+1..4 keybinds use; the button fixture confirms this half is genuinely armed, but the floating-window fixture authors no matching listener-registration property, so the generic mechanism has no proven target in the data on hand. Per CLAUDE.md, the user's retail memory is the axiom regardless: ChatWindowController.BindIndicatorClicks wires each indicator's click through the same ToggleFloatingChatWindow chokepoint the keybinds use, as explicit user-directed retail behavior. SetIndicatorOpen stays the sole writer of the Selected mirror so the visual stays consistent through the click round trip. Full reconciliation in docs/research/2026-08-09-chat-retail-window-shell.md §1.4. Campaign plan gets the round-4 findings section; items 1+2 (text-style) are under parallel research, item 4 passed, item 6 deferred to the settings track. Suite: 12,579 passed / 4 skipped / 0 failed (Release, complete solution), up from baseline 12,553/4/0 — net +26 tests, zero regressions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
8b166f3ea2
commit
5b54387b8e
10 changed files with 1129 additions and 239 deletions
|
|
@ -76,8 +76,16 @@ public class ChatLayoutConformanceTests
|
|||
[InlineData(0x10000523u)]
|
||||
[InlineData(0x10000524u)]
|
||||
[InlineData(0x10000525u)]
|
||||
public void MountedChatWindow_IndicatorButtons_ImportVisibleAndInert(uint indicatorId)
|
||||
public void MountedChatWindow_IndicatorButtons_BindAloneLeavesClickUnwiredButSelfFlipSuppressed(
|
||||
uint indicatorId)
|
||||
{
|
||||
// Bind() alone (no BindIndicatorClicks call) leaves OnClick unwired —
|
||||
// the click half is a separate opt-in step RetailUiRuntime performs
|
||||
// once its own ToggleFloatingChatWindow method is available (round 4,
|
||||
// 2026-08-10 — see ChatWindowController.SetIndicatorOpen's doc for the
|
||||
// full retail mechanism reconciliation). This test only proves the
|
||||
// Bind-alone state; MountedChatWindow_IndicatorButtons_ClickTogglesWindow
|
||||
// below exercises the full click round trip through BindIndicatorClicks.
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
|
|
@ -87,21 +95,18 @@ public class ChatLayoutConformanceTests
|
|||
UiElement? indicator = layout.FindElement(indicatorId);
|
||||
Assert.NotNull(indicator);
|
||||
Assert.True(indicator!.Visible);
|
||||
// Inert: CH6a does not wire a click handler (CH6b's job — the
|
||||
// one-directional visibility mirror has no button-press behavior of
|
||||
// its own in retail either, per the research doc §1.4). UiButton
|
||||
// structurally always reports HandlesClick=true (it can receive the
|
||||
// event), but with no OnClick/OnClickAt bound, a click is a no-op.
|
||||
var button = Assert.IsType<UiButton>(indicator);
|
||||
Assert.Null(button.OnClick);
|
||||
Assert.Null(button.OnClickAt);
|
||||
|
||||
// CH6a/b REJECT-review SHOULD-FIX 3: OnClick==null is the wrong level
|
||||
// to prove "inert" — the fixture's own 0x0B (ToggleBehavior) = true
|
||||
// means a plain press/release would still flip UiButton's internal
|
||||
// Selected mirror even with no OnClick bound. SuppressSelfToggle
|
||||
// (set by ChatWindowController.Bind) is the actual guard; a press
|
||||
// and release must leave Selected unchanged.
|
||||
// to prove "self-flip suppressed" — the fixture's own 0x0B
|
||||
// (ToggleBehavior) = true means a plain press/release would still
|
||||
// flip UiButton's internal Selected mirror even with no OnClick
|
||||
// bound. SuppressSelfToggle (set by ChatWindowController.Bind) is
|
||||
// the actual guard, and it stays true even after round 4 wires a
|
||||
// real click handler — see SetIndicatorOpen's doc for why the blind
|
||||
// self-flip must stay suppressed regardless.
|
||||
Assert.True(button.SuppressSelfToggle);
|
||||
bool before = button.Selected;
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
|
|
@ -109,6 +114,83 @@ public class ChatLayoutConformanceTests
|
|||
Assert.Equal(before, button.Selected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x10000522u, 1)]
|
||||
[InlineData(0x10000523u, 2)]
|
||||
[InlineData(0x10000524u, 3)]
|
||||
[InlineData(0x10000525u, 4)]
|
||||
public void MountedChatWindow_IndicatorButtons_ClickTogglesWindow_ThroughBindIndicatorClicks(
|
||||
uint indicatorId, int expectedWindowId)
|
||||
{
|
||||
// Round 4 (2026-08-10): clicking an indicator now toggles its floating
|
||||
// chat window through the SAME chokepoint the Alt+1..4 keybinds use
|
||||
// (user-directed retail behavior — see SetIndicatorOpen's doc for the
|
||||
// full mechanism reconciliation: the generic UIElement_Button
|
||||
// action-dispatch system is real and armed on the button side, but
|
||||
// the shipped floating-window fixture authors no matching listener
|
||||
// registration, so this wiring cannot be proven purely from the DAT).
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
infos, layout, new ChatVM(new ChatLog()), () => NullCommandBus.Instance, new ChatWindowState(), null, null, NoTex);
|
||||
Assert.NotNull(controller);
|
||||
|
||||
var toggled = new List<int>();
|
||||
controller!.BindIndicatorClicks(windowId =>
|
||||
{
|
||||
toggled.Add(windowId);
|
||||
return true;
|
||||
});
|
||||
|
||||
UiElement? indicator = layout.FindElement(indicatorId);
|
||||
var button = Assert.IsType<UiButton>(indicator);
|
||||
Assert.NotNull(button.OnClick);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
|
||||
Assert.Equal(new[] { expectedWindowId }, toggled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MountedChatWindow_IndicatorButtons_ClickRoundTrip_KeepsMirrorConsistent()
|
||||
{
|
||||
// The full round trip: click -> BindIndicatorClicks' delegate calls the
|
||||
// real toggle -> the caller (standing in for RetailUiRuntime's
|
||||
// WindowVisibilityChanged plumbing) calls SetIndicatorOpen with the
|
||||
// window's NEW state -> Selected matches that new state, not a blind
|
||||
// self-flip. SuppressSelfToggle staying true is what makes this
|
||||
// authoritative-writer invariant hold even though the button now has
|
||||
// a real OnClick.
|
||||
var infos = FixtureLoader.LoadChatInfos();
|
||||
var layout = LayoutImporter.Build(infos, NoTex, null);
|
||||
var controller = ChatWindowController.Bind(
|
||||
infos, layout, new ChatVM(new ChatLog()), () => NullCommandBus.Instance, new ChatWindowState(), null, null, NoTex);
|
||||
Assert.NotNull(controller);
|
||||
|
||||
bool windowOpen = false;
|
||||
controller!.BindIndicatorClicks(windowId =>
|
||||
{
|
||||
windowOpen = !windowOpen;
|
||||
controller.SetIndicatorOpen(windowId, windowOpen);
|
||||
return true;
|
||||
});
|
||||
|
||||
var button = Assert.IsType<UiButton>(layout.FindElement(0x10000522u));
|
||||
Assert.False(button.Selected);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
Assert.True(button.Selected);
|
||||
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseDown, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.MouseUp, Data1: 2, Data2: 2));
|
||||
button.OnEvent(new UiEvent(0, button, UiEventType.Click, Data1: 2, Data2: 2));
|
||||
Assert.False(button.Selected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x10000693u)]
|
||||
[InlineData(0x10000694u)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue