fix #385: Options dropdowns — white centered text + size-to-content popup

User gate report (Campaign OP happy-testing round, 2026-08-13): every
Config-tab dropdown drew its text gold + left-aligned and its popup a
fixed 6 rows regardless of item count. All three were unmeasured styling
divergences — the authored data (new probe menuprobe3, live DAT) says:

- button label child 0x10000355: fontColor white, hJustify=Center
- row template 0x1000035A: fontColor white, hJustify=Center
- popup ListBox 0x10000358: edge-docked L=T=R=B=1, the authored condition
  arming retail UIElement_Menu::RecalculatePopupSize @0x0046caf0 —
  popup resizes to the ListBox's summed content height, uncapped
  (0x0046e5f4..0046e66c via ResizeScrollableArea's 0x32 broadcast)

UiMenu gains three opt-in properties (ButtonTextCentered,
ItemTextCentered, PopupSizeToContent) plus retail Open @0x0046cc42's
empty-list gate; chat + vendor keep the class defaults, so their shipped
behavior is untouched. ConfigOptionsPageController.ApplyMenuChrome wires
all four corrections for the 8 Config menus with the probe citation.

The same probe found vendor's authored popup ListBox is ALSO docked while
our vendor dropdown ships G5's fixed 6-row window — filed as #386 +
register row AD-88 (UNCLEAR: the G5 retail screenshot and the decomp
mechanism conflict) instead of silently reworking a user-gated surface.

The "resolution change resizes the window" observation from the same
report is #374's designed windowed-mode behavior (display-mode switching
is #376/#377) — no change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-13 08:51:57 +02:00
parent 028920420d
commit a9b6435f55
8 changed files with 422 additions and 9 deletions

View file

@ -489,4 +489,76 @@ public class UiMenuTests
Assert.Equal(0f, menu.TextIndent);
Assert.Equal(0f, menu.ButtonTextIndent);
}
// ------------------------------------------------------------------
// User gate report 2026-08-13: fixed-6-row popups for short option
// lists. Retail (UIElement_Menu::RecalculatePopupSize @0x0046caf0)
// sizes the popup to the ListBox's summed content height, uncapped,
// when the authored ListBox is edge-docked — see
// UiMenu.PopupSizeToContent's own doc for the full mechanism.
// ------------------------------------------------------------------
private static UiMenu MakeScrollableMenu(int itemCount, bool sizeToContent) => new UiMenu
{
Width = 120f, Height = 18f,
Scrollable = true,
OpenUpward = false,
RowsPerColumn = 6,
RowHeight = 18f,
ColumnWidth = 100f,
PopupSizeToContent = sizeToContent,
Items = Enumerable.Range(0, itemCount)
.Select(i => new UiMenu.MenuItem($"Item {i}", (object?)i)).ToArray(),
};
[Fact]
public void SizeToContent_ShrinksThePopupToTheItemCount()
{
// 3 items: interior 3*18, plus the 5px bevel top+bottom.
Assert.Equal(3 * 18f + 10f, MakeScrollableMenu(3, sizeToContent: true).PopupOuterHeight);
// Control: without size-to-content the fixed 6-row window stands.
Assert.Equal(6 * 18f + 10f, MakeScrollableMenu(3, sizeToContent: false).PopupOuterHeight);
}
[Fact]
public void SizeToContent_GrowsPastTheFixedWindow_AndTheLastRowIsPickable()
{
// Retail's content sum is UNCAPPED — 9 items = 9 rows, no scrolling.
UiMenu menu = MakeScrollableMenu(9, sizeToContent: true);
Assert.Equal(9 * 18f + 10f, menu.PopupOuterHeight);
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open
Assert.True(menu.IsOpen);
object? fired = null;
menu.OnSelect = p => fired = p;
// Row 8 (the 9th item) sits past the old 6-row window: it must be
// directly pickable with NO scroll. Downward popup: interior starts
// at Height + border.
float ly = menu.Height + 5f + 8 * menu.RowHeight + menu.RowHeight / 2f;
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, (int)ly)));
Assert.Equal(8, fired);
Assert.False(menu.IsOpen); // picking closes, same as every other path
}
[Fact]
public void EmptyItems_ButtonClickDoesNotOpen()
{
// Retail UIElement_Menu::Open @0x0046cc42 gates on
// m_listBox->m_listItems.m_num != 0 — an itemless menu never opens.
UiMenu menu = MakeScrollableMenu(0, sizeToContent: true);
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5)));
Assert.False(menu.IsOpen);
}
[Fact]
public void TextStyleDefaults_PreserveChatAndVendorBehavior()
{
// The three 2026-08-13 additions are opt-in: chat's gold left-aligned
// caption and vendor's fixed 6-row window are untouched by default.
var menu = new UiMenu();
Assert.False(menu.ButtonTextCentered);
Assert.False(menu.ItemTextCentered);
Assert.False(menu.PopupSizeToContent);
}
}