fix(D.2b): behavioral widgets are leaf — ConsumesDatChildren (chat menu open)

The generalized channel menu wouldn't open: the factory recursed the Type-6
menu element's dat children, building its invisible Type-12 label child as a
UiText. Hit-testing is children-first and UiText consumes MouseDown (selection),
so the label child swallowed the menu button click and the dropdown never opened.
The transcript similarly gained an invisible Ghosted-button child (a 16x16
selection dead-zone). The old hand-made build never had these — it skipped Type 12
and hand-placed the widgets with no children.

Fix: behavioral widgets (Meter/Menu/Button/Scrollbar/Text/Field) draw their full
appearance and reproduce their dat sub-elements procedurally, so they are LEAF —
the importer must not build their dat children as separate (click-stealing)
widgets. Add UiElement.ConsumesDatChildren (default false; the 6 behavioral
widgets override true) and gate LayoutImporter recursion on it (replacing the
UiMeter-only special case). Only generic containers (UiDatElement, panels) recurse.

Visually confirmed in the live client (channel menu opens; General/Trade selected
and sent). Vitals unchanged (UiMeter was already leaf). Full suite: 404 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-16 18:36:40 +02:00
parent 83076cdbb6
commit d7002552bc
8 changed files with 45 additions and 5 deletions

View file

@ -80,6 +80,10 @@ public sealed class UiMenu : UiElement
public UiMenu() { CapturesPointerDrag = true; }
/// <summary>The menu draws its own button face + popup; its dat label/row children
/// must NOT be built (an invisible label child would intercept the button click).</summary>
public override bool ConsumesDatChildren => true;
protected override void OnDraw(UiRenderContext ctx)
{
var resolve = SpriteResolve;