fix #382: chat-window indicator buttons invisible until first hovered
Root cause (found via reference-identity-verified live-DAT probing, not
a guess): the four main-chat-window indicator buttons (0x10000522-
0x10000525) resolve their own correct ActiveState="Normal" at
construction, then get blanked to "" moments later in the SAME
LayoutImporter.Build call. The indicator column's backing panel
(0x10000600) authors PassToChildren=true on its own empty DirectState
(confirmed live: States[0xFFFFFFFF].PassToChildren == true); when
LayoutImporter.BuildWidget's post-attach state reapply runs for that
panel, UiDatElement.TrySetRetailState cascades its DirectStateId to
every IUiDatStateful child, including the already-correctly-resolved
buttons. UiButton.TrySetRetailState's DirectStateId branch used to
accept that cascade because every button structurally carries a
DirectStateId entry in its States dict as a property bag (ToggleBehavior/
RolloverEnabled/etc), independent of whether it authors any blank
sprite, so TryFindState(DirectStateId) found that entry and blanked
ActiveState even with no "" media. A hover "fixed" it only because
UiButtonStateMachine.RequestedState resolves to the same canonical
Normal id regardless of PointerOver when RolloverEnabled is false.
Retail's own decompiled UIElement::SetState @0x00464e70 does the exact
same unconditional-commit-plus-cascade; retail avoids this specific bug
purely through construction timing (UIElement::Initialize's SetState
call precedes child-tree construction, so a cascade fired during import
always iterates zero children). Our port's LayoutImporter.BuildWidget
deliberately reapplies in the opposite order to give retained
PassToChildren tabs their authored child media, so this literal
state-machine port needed a compensating guard.
Fix: UiButton.TrySetRetailState's DirectStateId branch now requires
REAL "" media (HasStateMedia("")) before accepting the transition.
Scoped to UiButton only; UiDatElement's parallel branch and the cascade
mechanism are unchanged, so CharacterStatController's own
PassToChildren-driven chrome children are unaffected. Register row
AP-206 records the divergence from retail's literal unconditional-
commit semantics. Regressed by two fast unit tests in UiButtonTests.cs
(DirectStateCascade_WithoutRealMedia_DoesNotBlankAnAlreadyResolvedState,
DirectStateTransition_WithRealMedia_StillSucceeds) plus a live-mount
probe confirming all four buttons resolve ActiveState="Normal"
immediately after import against the real installed DAT.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a31fd631ad
commit
d1c60df946
6 changed files with 292 additions and 10 deletions
|
|
@ -162,6 +162,46 @@ public class UiButtonTests
|
|||
Assert.Equal("Normal", b.ActiveState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectStateCascade_WithoutRealMedia_DoesNotBlankAnAlreadyResolvedState()
|
||||
{
|
||||
// #382: LayoutImporter.BuildWidget's post-attach state reapply cascades a
|
||||
// PARENT's PassToChildren DirectState to EVERY IUiDatStateful child (the
|
||||
// chat window's indicator-button backing panel, 0x10000600, authors exactly
|
||||
// this). Every button structurally carries a DirectStateId entry in its own
|
||||
// States dict purely as the property bag for ToggleBehavior/RolloverEnabled/
|
||||
// etc (see AddBoolProperty below) — that structural presence must NOT be
|
||||
// enough to accept a DirectState transition when the button has no real ""
|
||||
// media, or an ancestor's unrelated cascade blanks an already-correct
|
||||
// "Normal" resolution before first paint.
|
||||
var info = ButtonInfo("Normal", "Highlight");
|
||||
AddBoolProperty(info, 0x13u, true); // RolloverEnabled — populates States[DirectStateId]
|
||||
var b = CreateButton(info);
|
||||
Assert.Equal("Normal", b.ActiveState);
|
||||
|
||||
bool ok = b.TrySetRetailState(UiStateInfo.DirectStateId);
|
||||
|
||||
Assert.False(ok);
|
||||
Assert.Equal("Normal", b.ActiveState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectStateTransition_WithRealMedia_StillSucceeds()
|
||||
{
|
||||
// The companion positive case: a button that legitimately authors ""
|
||||
// (DirectState) media must still be able to transition to it explicitly —
|
||||
// the fix narrows the check to "has real media", it does not disable the
|
||||
// DirectState branch outright.
|
||||
var info = ButtonInfo("Normal");
|
||||
info.StateMedia[""] = (7u, 1);
|
||||
var b = CreateButton(info);
|
||||
|
||||
bool ok = b.TrySetRetailState(UiStateInfo.DirectStateId);
|
||||
|
||||
Assert.True(ok);
|
||||
Assert.Equal("", b.ActiveState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HotClick_FiresImmediatelyRepeatsAndSuppressesReleaseClick()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue