fix(ui): #375 residual — activate the Configure Keyboard screen's own tab control

The gate-3 screenshot flight showed the keyboard list still rendering as
overlapping text after the string-resolver + parked-prototype fixes: all
SIX ActionClass pages were visible simultaneously (six stacked ListBoxes
— 'EmotesterSettings' is the Emote and CharacterSettings pages
interleaved) with six dead tab buttons above them. Root cause: OP8's
Bind built every page's rows but never called ActivateTabBehavior() on
the screen's own Type-8 tab host (0x1000049B), so no authored click
bindings were wired and no default-entry switch ran. The authored table
marks Movement (0x1000049D) IsDefault=true — activation now performs the
same default switch OptionsPanelController runs on ITS host, hiding the
other five pages and making the six tabs live.

Regressed by Bind_ActivatesTheTabControl_MovementDefaultShown_OtherPagesHidden
(fixture-driven: BehaviorActive, Movement visible, five pages hidden,
SwitchTo flips exclusivity).

Full Release suite: 13,087 passed / 4 skipped / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-11 16:11:22 +02:00
parent 3441a71833
commit a8ce010d02
2 changed files with 71 additions and 0 deletions

View file

@ -128,6 +128,53 @@ public sealed class KeyboardConfigControllerTests
Assert.Equal(3, controller.Page.Rows.Count);
}
[Fact]
public void Bind_ActivatesTheTabControl_MovementDefaultShown_OtherPagesHidden()
{
// Gate-2 screenshot finding (#375 residual): OP8 never activated the
// screen's own Type-8 tab control, so all six ActionClass pages stayed
// Visible simultaneously — six stacked ListBoxes reading as
// "overlapping text", with dead tab buttons. Bind must run the
// authored default-entry switch (Movement, IsDefault=true) exactly
// like OptionsPanelController does for its own host.
var snapshot = new RetailActionMapSnapshot(new[]
{
Row(0x4, 0x29, RetailActionClass.Movement),
Row(0x5, 0x33, RetailActionClass.Camera),
});
ImportedLayout layout = FixtureLoader.LoadKeyboardConfig();
var fake = new FakeBindings();
KeyboardConfigController controller = KeyboardConfigController.Bind(
layout, snapshot, MakeTemplateResolver(), (_, _) => null, fake.ToBindings())!;
Assert.NotNull(controller);
UiTabPanel tabHost = Assert.IsType<UiTabPanel>(layout.FindElement(0x1000049Bu));
Assert.True(tabHost.BehaviorActive);
Assert.Equal(0x1000049Du, tabHost.ActivePageElementId); // Movement, the authored default
// Exactly one page visible: Movement; the other five hidden.
foreach ((uint pageId, bool expectVisible) in new[]
{
(0x1000049Du, true), // Movement
(0x1000049Fu, false), // Camera
(0x100004A1u, false), // Combat
(0x100004A3u, false), // UI
(0x10000211u, false), // CharacterSettings
(0x100004A5u, false), // Emote
})
{
UiElement? page = UiElement.FindDescendant(tabHost, pageId);
Assert.NotNull(page);
Assert.Equal(expectVisible, page!.Visible);
}
// The tab buttons are live: switching to Camera flips exclusivity.
tabHost.SwitchTo(0x1000049Fu);
Assert.False(UiElement.FindDescendant(tabHost, 0x1000049Du)!.Visible);
Assert.True(UiElement.FindDescendant(tabHost, 0x1000049Fu)!.Visible);
}
[Fact]
public void Bind_MapsKnownActionsAndLeavesUnknownOnesUnmapped()
{