fix(chat): CH6c review fixes — opaque default, opacity-transition register clauses
BLOCKER: ChatSettings.DefaultOpacity shipped retail's base ChatInterface value (0.5) as ONE shared global default applied to every RetailWindowManager-registered window, not just the four floating chat windows retail itself fades. That faded the whole out-of-box registered UI (radar, vitals, toolbar, main chat, ...) to 50% opacity, including several windows that can never take keyboard focus and so were stuck at 0.5 permanently. Fixed to gmMainChatUI's 1.0/1.0 override (0x004CD0F0) instead — retail-identical opaque presentation for the 11 non-chat windows and the main chat window; only the four floating chat windows now diverge from retail's 0.5-while-idle default, and the Settings -> Chat transparency slider remains fully user-settable. AP-190 reworded and gains two new decomp-verified clauses: (3) retail eases opacity toward its target by 5% of the delta per tick (ChatInterface::ListenToGlobalMessage @0x004F3840, armed from the focus element-messages at @0x004F5275) where acdream snaps -- deferred, needs a UI frame-tick hook the opacity controller doesn't have; (4) retail's focus predicate is the chat ENTRY FIELD specifically (ChatInterface::IsTextEntryFocused @0x004F30A0) where acdream uses any-focusable-descendant. Both findings + the pre-existing UiMenu.cs PushAlphaAbsolute(1f) popup bypass are folded into the window-shell research doc's opacity section. NITs: fixed the stale "text bypasses the alpha" comment in UiElement.DrawSelfAndChildren (CH6c already routed DrawStringDat/ DrawString through the same ApplyAlpha chokepoint as sprites/rects); added RetailWindowManager.WindowUnregistered + wired RetailWindowOpacityController to detach and forget a window unregistered while it held focus (previously only Dispose detached, leaking any window unregistered mid-focus for the rest of the session); added post-Dispose no-op guards to the three Set* opacity mutators; added a DrawString (BitmapFont path) alpha regression test and a DrawStringDat outline/background-pass alpha test (the existing tests only ever exercised the foreground/fill pass). Also fixes RuntimeSettingsControllerTests.SettingsViewModelSavePreserves SectionAndTargetOrder's now-stale "target-chat-opacity:0.5:1" expectation (caught by the full-suite run this fix requires) to match the new 1.0 default. Campaign ledger CH6c row updated to APPROVE-WITH-FIXES. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
964af62e25
commit
cc58289967
14 changed files with 319 additions and 18 deletions
|
|
@ -185,4 +185,63 @@ public sealed class RetailWindowOpacityControllerTests
|
|||
// focus change after Dispose is not observed anymore.
|
||||
Assert.Equal(0.5f, handle.Opacity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WindowUnregistered_DetachesSubscription_AndForgetsFocusedState()
|
||||
{
|
||||
// CH6c review NIT: before this fix, the only detach point for a
|
||||
// handle's DescendantFocusChanged subscription (and its membership in
|
||||
// _focused) was the CONTROLLER's own Dispose — a window unregistered
|
||||
// while it held focus stayed subscribed and pinned in _focused for
|
||||
// the rest of the session. Prove the RetailWindowManager.WindowUnregistered
|
||||
// wiring actually detaches: a stray post-unregister notification (the
|
||||
// kind a lingering external reference to the handle could still fire)
|
||||
// must not reach the controller anymore.
|
||||
UiRoot root = NewRoot();
|
||||
(RetailWindowHandle handle, UiElement child) = RegisterWindow(root, "Chat");
|
||||
var controller = new RetailWindowOpacityController(
|
||||
root.WindowManager, defaultOpacity: 0.5f, activeOpacity: 1.0f);
|
||||
|
||||
root.SetKeyboardFocus(child);
|
||||
Assert.Equal(1.0f, handle.Opacity);
|
||||
|
||||
root.WindowManager.Unregister("Chat");
|
||||
|
||||
// Unregister hides the outer frame, which drops keyboard focus off
|
||||
// the now-invisible child — the manager's (still-live at that point)
|
||||
// focus-change plumbing reapplies DefaultOpacity naturally. Expected
|
||||
// either way; not itself the thing this test pins.
|
||||
Assert.Equal(0.5f, handle.Opacity);
|
||||
|
||||
controller.SetActiveOpacity(0.7f);
|
||||
|
||||
// A stray post-unregister focus-gain notification (the kind a
|
||||
// lingering external reference to the handle could still fire). If
|
||||
// the controller were STILL subscribed, this would re-add the stale
|
||||
// handle to _focused and apply the NEW active opacity (0.7).
|
||||
handle.NotifyDescendantFocusChanged(child);
|
||||
|
||||
Assert.Equal(0.5f, handle.Opacity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetMutators_AfterDispose_AreNoOps()
|
||||
{
|
||||
UiRoot root = NewRoot();
|
||||
(RetailWindowHandle handle, _) = RegisterWindow(root, "Chat");
|
||||
var controller = new RetailWindowOpacityController(
|
||||
root.WindowManager, defaultOpacity: 0.5f, activeOpacity: 1.0f);
|
||||
|
||||
controller.Dispose();
|
||||
controller.SetDefaultOpacity(0.9f);
|
||||
controller.SetActiveOpacity(0.9f);
|
||||
controller.SetOpacity(0.2f, 0.3f);
|
||||
|
||||
// None of the three post-Dispose calls changed anything — no
|
||||
// ObjectDisposedException either, matching Dispose's own idempotent
|
||||
// shape.
|
||||
Assert.Equal(0.5f, controller.DefaultOpacity);
|
||||
Assert.Equal(1.0f, controller.ActiveOpacity);
|
||||
Assert.Equal(0.5f, handle.Opacity);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue