fix(ui): talk button keeps authored 46x17; white authored captions + font for talk/Send

Owner report (2026-08-24, post gate-pass): the chat channel button was
bigger than retail and both its caption and the Send caption were warm
gold instead of retail's near-white.

Size: retail never resizes the talk button — HandleSelection
@0x004cd540 only swaps the caption string; the authored 46x17 element
stands, and the authored SHORT captions ('Gen', 'Fell', ...) fit it —
that is why retail abbreviates. Our content-widening reflow (grow the
button to its label, shift the input) was a compensation for the
now-retired invented long captions, measured with the wrong font on
top. Deleted; the authored row layout stands.

Color + font: the button caption child (0x10000015) and the Send
button (0x10000019) both author pure white text with their OWN FontDid
0x40000002 (live-DAT probed) — different from the transcript font,
which is the other half of why 'Chat' fits 46px. UiMenu gains a
ButtonDatFont for the caption (popup rows keep the menu font);
the controller reads both elements' authored FontColor/FontDid instead
of the invented (1,.92,.72) constants.

The old widening pin is rewritten to the retail contract; a new
conformance test pins authored width, white captions, and the authored
font DID being requested for both buttons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-24 20:43:46 +02:00
parent c12a95b6e8
commit 67e963524b
4 changed files with 106 additions and 33 deletions

View file

@ -202,6 +202,17 @@ public sealed class UiMenu : UiElement
public uint CurrentFaceSpriteForTest => _facePressed ? PressedSprite : NormalSprite;
public UiDatFont? DatFont { get; set; }
/// <summary>
/// Optional separate font for the BUTTON caption. The chat talk button's
/// authored label child (0x10000015) carries its own FontDid 0x40000002 —
/// different from the popup rows/transcript font — which is how retail
/// fits 'Chat'/'Gen' inside the authored 46x17 face (live-DAT probed
/// 2026-08-24, the owner-reported "button too big" delta). Null falls
/// back to <see cref="DatFont"/>.
/// </summary>
public UiDatFont? ButtonDatFont { get; set; }
public AcDream.App.Rendering.BitmapFont? Font { get; set; }
/// <summary>Retail LayoutDesc property <c>0x21</c> (two-pass glyph outline,
@ -400,10 +411,17 @@ public sealed class UiMenu : UiElement
// label child spans the button MINUS the arrow-cap overlay's right
// socket (0x10000355 is 100 wide of the 117 button, docked; the
// arrow child overlays the last 17px — menuprobe3).
UiDatFont? captionFont = ButtonDatFont ?? DatFont;
float captionW = captionFont?.MeasureWidth(caption)
?? Font?.MeasureWidth(caption) ?? caption.Length * 7f;
float captionLineH = captionFont?.LineHeight ?? Font?.LineHeight ?? 14f;
float capX = ButtonTextCentered
? MathF.Max(0f, (Width - (ArrowCapClosedSprite != 0 ? ArrowCapWidth : 0f) - MeasureText(caption)) * 0.5f)
? MathF.Max(0f, (Width - (ArrowCapClosedSprite != 0 ? ArrowCapWidth : 0f) - captionW) * 0.5f)
: ButtonTextIndent;
DrawLabel(ctx, caption, capX, (Height - LineH()) * 0.5f, TextColor);
if (captionFont is { } cf)
ctx.DrawStringDat(cf, caption, capX, (Height - captionLineH) * 0.5f, TextColor, Outline, OutlineColor);
else
ctx.DrawString(caption, capX, (Height - captionLineH) * 0.5f, TextColor, Font);
// G6: the open/closed arrow-cap overlay — see ArrowCapClosedSprite's doc comment.
if (resolve is not null) DrawArrowCap(ctx, resolve);
@ -443,7 +461,8 @@ public sealed class UiMenu : UiElement
public float NaturalButtonWidth()
{
string text = ButtonLabelProvider?.Invoke() ?? "";
float textW = DatFont?.MeasureWidth(text) ?? Font?.MeasureWidth(text) ?? text.Length * 7f;
UiDatFont? nf = ButtonDatFont ?? DatFont;
float textW = nf?.MeasureWidth(text) ?? Font?.MeasureWidth(text) ?? text.Length * 7f;
return ButtonTextIndent + textW + 4f + FaceCapR; // text start (clears LED) + text + gap + arrow cap
}