fix #378: Config-tab dropdown menus render bare with no popup chrome
Root cause: DatWidgetFactory builds Config-tab Type-0x10000038 menu leaves as bare UiMenu instances (matching the vendor/chat channel menu pattern), but unlike those two controllers, ConfigOptionsPageController never wired the menu's sprite/font/geometry properties after Bind — so every dropdown rendered as plain text with no button well, no arrow cap, and opened no popup on click (#374's fix only corrected click ROUTING, not the missing chrome). Fix: ConfigOptionsPageController.ApplyMenuChrome wires every Config-tab menu row with the SAME retail sprite ids VendorUiController/ ChatWindowController's channel menu already use for this shared popup catalog (LayoutDesc 0x21000043), verified against the live DAT via OptionsPanelLiveMountProbeTests' ProbeConfigMenuChrome/ ProbeConfigMenuPopupChrome probes. Regressed by ConfigOptionsPageControllerTests.MenuRow_SoundFeatures_OpensAndSelects ThroughRealHitPath_UsingAuthoredPopupGeometry, which drives the real click-to-open + item-pick path through the authored popup geometry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
28bef4e03b
commit
c121842664
6 changed files with 524 additions and 35 deletions
|
|
@ -320,6 +320,23 @@ public sealed class ConfigOptionsPageControllerTests
|
|||
return (controller, fakeBindings, bound);
|
||||
}
|
||||
|
||||
/// <summary>Every built <see cref="UiMenu"/> under a subtree, in
|
||||
/// document (build) order — the same recursive-walk shape
|
||||
/// <c>ChatOptionsPageControllerTests.CollectScalarSliders</c> uses for
|
||||
/// its own widget-type collection.</summary>
|
||||
private static List<UiMenu> CollectMenus(UiElement root)
|
||||
{
|
||||
var found = new List<UiMenu>();
|
||||
Walk(root, found);
|
||||
return found;
|
||||
|
||||
static void Walk(UiElement node, List<UiMenu> acc)
|
||||
{
|
||||
if (node is UiMenu menu) acc.Add(menu);
|
||||
foreach (UiElement child in node.Children) Walk(child, acc);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bind_Succeeds_AndRegistersExactly30Rows()
|
||||
{
|
||||
|
|
@ -466,6 +483,87 @@ public sealed class ConfigOptionsPageControllerTests
|
|||
Assert.Equal(1, bindings.AudioSaves[^1].SoundFeatures);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #378 regression (2026-08-11, gate 4): before this fix, NOTHING wired
|
||||
/// any Config-tab dropdown's chrome — every sprite id was 0, no arrow
|
||||
/// cap, no popup. Drives the REAL <see cref="UiMenu"/> hit-test/pick
|
||||
/// path <see cref="VendorUiControllerTests.CategoryMenu_OpensAndSelectsThroughRealHitPath_UsingAuthoredPopupGeometry"/>
|
||||
/// already established for vendor's own dropdown — the SAME
|
||||
/// authored popup catalog (LayoutDesc <c>0x21000043</c>) both dropdowns
|
||||
/// derive from, byte-verified identical (see
|
||||
/// <see cref="ConfigOptionsPageController.MenuChromeSprites"/>'s own
|
||||
/// doc), so the SAME click-geometry formula applies.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MenuRow_SoundFeatures_OpensAndSelectsThroughRealHitPath_UsingAuthoredPopupGeometry()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadOptionsPanelHost();
|
||||
OptionsPanelController controller = OptionsPanelController.Bind(
|
||||
layout,
|
||||
new OptionsPanelController.Callbacks(
|
||||
Toggle: () => { },
|
||||
RequestExitToCharacterSelection: () => { },
|
||||
ExitGame: () => { },
|
||||
UseMouseTurningSettings: () => { },
|
||||
DisplaySystemMessage: _ => { }))!;
|
||||
var fakeBindings = new FakeBindings();
|
||||
bool bound = ConfigOptionsPageController.Bind(
|
||||
layout,
|
||||
controller.ConfigPage,
|
||||
MakeTemplateResolver(),
|
||||
(_, _) => null,
|
||||
fakeBindings.ToBindings(),
|
||||
resolveSprite: _ => (1u, 8, 8));
|
||||
Assert.True(bound);
|
||||
|
||||
var listBox = Assert.IsType<UiTemplateListBox>(
|
||||
layout.FindElement(ConfigOptionsPageController.ListBoxElementId));
|
||||
List<UiMenu> menus = CollectMenus(listBox);
|
||||
Assert.Equal(8, menus.Count); // the 8 Config-tab dropdown rows
|
||||
UiMenu soundFeatures = menus[0]; // build order matches Rows order — Sound Features first
|
||||
|
||||
// Wiring: without these UiMenu.OnDrawOverlay early-returns (nothing
|
||||
// renders) and the button face never draws either — the bare-text
|
||||
// symptom #378 reported.
|
||||
Assert.NotNull(soundFeatures.SpriteResolve);
|
||||
Assert.NotEqual(0u, soundFeatures.NormalSprite);
|
||||
Assert.NotEqual(0u, soundFeatures.PressedSprite);
|
||||
Assert.NotEqual(0u, soundFeatures.ItemNormalSprite);
|
||||
Assert.NotEqual(0u, soundFeatures.ItemHighlightSprite);
|
||||
Assert.NotEqual(0u, soundFeatures.ArrowCapClosedSprite);
|
||||
Assert.NotEqual(0u, soundFeatures.ArrowCapOpenSprite);
|
||||
|
||||
// Geometry from the live-DAT-verified popup catalog (0x21000043) —
|
||||
// see MenuChromeSprites' own doc.
|
||||
Assert.True(soundFeatures.Scrollable);
|
||||
Assert.Equal(6, soundFeatures.RowsPerColumn);
|
||||
Assert.Equal(18f, soundFeatures.RowHeight);
|
||||
Assert.Equal(100f, soundFeatures.ColumnWidth);
|
||||
Assert.False(soundFeatures.OpenUpward); // no authored attribute 5 — absent defaults false
|
||||
|
||||
Assert.False(soundFeatures.IsOpen);
|
||||
|
||||
// Open via the real widget event path (button click).
|
||||
Assert.True(soundFeatures.OnEvent(new UiEvent(0, soundFeatures, UiEventType.MouseDown, 0, 10, 5)));
|
||||
Assert.True(soundFeatures.IsOpen);
|
||||
|
||||
// "Mono" is the SECOND choice (Stereo, then Mono) -> row index 1.
|
||||
// Popup opens DOWNWARD (OpenUpward=false), so its top sits at the
|
||||
// button's own bottom edge (ly = Height) — same formula
|
||||
// VendorUiControllerTests already established for the identical
|
||||
// popup catalog.
|
||||
const int border = 5; // RetailChromeSprites.Border (UiMenu's private bevel thickness)
|
||||
const int targetRow = 1;
|
||||
float iy = targetRow * soundFeatures.RowHeight + soundFeatures.RowHeight / 2f;
|
||||
float ly = soundFeatures.Height + iy + border;
|
||||
|
||||
Assert.True(soundFeatures.OnEvent(new UiEvent(0, soundFeatures, UiEventType.MouseDown, 0, 10, (int)ly)));
|
||||
|
||||
Assert.False(soundFeatures.IsOpen); // picking a row closes the popup
|
||||
Assert.Equal(1, fakeBindings.Audio.SoundFeatures);
|
||||
Assert.Equal(1, fakeBindings.AudioSaves[^1].SoundFeatures);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MenuRow_Resolution_IsStringBacked_AndWritesThroughDisplayBindings()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue