fix(chat): the unseen-text indicator follows its authored per-state visibility
Regression from cbab79d7, which I introduced: the indicator stopped showing at
all. Switching it from Visible to state-driven was half a correction — right
about retail's mechanism, wrong about what makes this element appear.
Measured, rather than reasoned about (LayoutDump gained --props for it):
0x1000048C state 13 Ghosted 0x3B = True -> hidden
state 1 Normal 0x3B = False -> shown
state 3 pressed 0x3B = False
Dat property 0x3B is "Invisible", authored PER STATE, and it is what puts this
element on screen. UiDatElement applies 0x3B on a state change; UiButton does
not, and this element builds as a button — so driving the state alone left it
hidden forever. The original Visible toggle was, by coincidence, exactly what
the authored data prescribes.
So the property is applied here rather than left unhonoured. That is the
authored data, not a visibility hack layered over the state machinery.
The state is still set, for the media it selects, but only on the way IN:
TrySetRetailState(Ghosted) means Enabled = false, and disabling the button
would also refuse the click that scrolls to the newest text — a second bug
waiting behind the first.
The test now pins VISIBILITY across the transitions instead of ActiveState.
The previous test passed while the feature was broken because the fixture
element carried no 0x3B, so the assertion could never see the property that
actually decides this. It fails now if the state is driven without the
visibility.
Proper fix noted for later: UiButton should honour per-state 0x3B the way
UiDatElement already does. That is a wider change than this regression wants.
Solution builds clean; full hermetic gate green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7aae5ba939
commit
a330d50df9
3 changed files with 58 additions and 26 deletions
|
|
@ -940,14 +940,28 @@ public sealed class ChatWindowController : IRetainedWindowStateController, IReta
|
|||
/// </remarks>
|
||||
private void SetUnreadIndicatorState(bool unread)
|
||||
{
|
||||
if (_unreadIndicator is not IUiDatStateful stateful)
|
||||
if (_unreadIndicator is null)
|
||||
return;
|
||||
stateful.TrySetRetailState(
|
||||
unread ? UiButtonStateMachine.Normal : GhostedStateId);
|
||||
}
|
||||
|
||||
/// <summary>Retail state <c>0xD</c>, the id its own click handler sets.</summary>
|
||||
private const uint GhostedStateId = 13u;
|
||||
// Apply the element's OWN authored per-state visibility (dat property
|
||||
// 0x3B, "Invisible"), measured on 0x1000048C as:
|
||||
//
|
||||
// state 13 Ghosted 0x3B = True -> hidden
|
||||
// state 1 Normal 0x3B = False -> shown
|
||||
//
|
||||
// UiDatElement applies 0x3B on a state change; UiButton does not, and
|
||||
// this element builds as a button. So the property is applied here
|
||||
// rather than left unhonoured — this is the authored data, not a
|
||||
// visibility hack layered over it.
|
||||
_unreadIndicator.Visible = unread;
|
||||
|
||||
// Set the state too, for the media it selects. Deliberately only on
|
||||
// the way IN: TrySetRetailState(Ghosted) means Enabled = false, and
|
||||
// disabling the button would also refuse the click that scrolls to
|
||||
// the newest text.
|
||||
if (unread && _unreadIndicator is IUiDatStateful stateful)
|
||||
stateful.TrySetRetailState(UiButtonStateMachine.Normal);
|
||||
}
|
||||
|
||||
/// <summary>Aims the chat entry at <paramref name="name"/> and focuses it.</summary>
|
||||
internal void StartTell(string name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue