feat(D.2b): UiMenu (Type 6) — generic dropdown; channel knowledge moves to controller (widget-generalization Task 4)

UiChannelMenu → UiMenu: removed ChatChannelKind, the 14-item array, the
button-text map, and the availability default. Generic surface: MenuItem
(label + object? Payload), Selected (object?), OnSelect, EnabledProvider,
ButtonLabelProvider, RowsPerColumn/RowHeight/ColumnWidth (all settable).
All draw/event mechanics unchanged — same popup geometry, same click
coordinates, same 8-piece bevel, same 3-slice button face.

ChatWindowController gains ChannelItems[], ChannelButtonLabel(), and
ChannelAvailable() (verbatim from old widget), and populates the
factory-built Type-6 UiMenu via find-by-id rather than constructing a
replacement widget. The Menu property type is now UiMenu. OnChannelChanged
wrap replaced with the generic OnSelect wrap for the ReflowInputRow hook.

DatWidgetFactory registers Type 6 → new UiMenu().

Tests: UiChannelMenuTests → UiMenuTests (10 tests, all green); factory
Type6 test added; ChatWindowControllerTests updated to use OnSelect.
Divergence register: AP-42 added (flat item model vs retail nested-submenu
MakePopup @0x46d310 — latent, unreachable through the chat menu).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-16 17:18:27 +02:00
parent 805ab5f40b
commit 955f7a69a8
8 changed files with 302 additions and 252 deletions

View file

@ -41,7 +41,7 @@ public class ChatWindowControllerTests
/// transcript (Type-12, no media) [0x10000011] ← skipped by factory
/// track (Type-3) [0x10000012]
/// inputBar (Type-3) [0x10000013]
/// menu (Type-3) [0x10000014]
/// menu (Type-6) [0x10000014]
/// input (Type-12, no media) [0x10000016] ← skipped by factory
/// send (Type-3) [0x10000019]
/// maxmin (Type-3) [0x1000046F]
@ -67,7 +67,7 @@ public class ChatWindowControllerTests
var menuNode = new ElementInfo
{
Id = 0x10000014u, Type = 3, X = 0, Y = 0, Width = 46, Height = 17,
Id = 0x10000014u, Type = 6, X = 0, Y = 0, Width = 46, Height = 17,
};
var inputNode = new ElementInfo
{
@ -180,8 +180,8 @@ public class ChatWindowControllerTests
var ctrl = ChatWindowController.Bind(rootInfo, layout, vm, () => bus, null, null, NoTex);
Assert.NotNull(ctrl);
// Switch channel to General.
ctrl!.Menu.OnChannelChanged!.Invoke(ChatChannelKind.General);
// Switch channel to General via the generic OnSelect (payload is ChatChannelKind).
ctrl!.Menu.OnSelect!.Invoke((object?)ChatChannelKind.General);
ctrl.Input.OnSubmit!.Invoke("hey all");
Assert.Single(bus.Published);