feat(D.2b): chat polish — typing fix, opacity, scrollbar 3-slice, retail channel menu

Visual-iteration batch (decomp-grounded), each fix verified against the retail screenshots:
- typing: UiElement.HitTest aborted on ClickThrough BEFORE walking children, so the
  ClickThrough UiDatElement panels blocked hit-testing to the input/transcript inside
  them. Check ClickThrough AFTER the child walk (it only gates whether THIS element
  claims the hit). Restores input focus + typing.
- opacity: UiElement.Opacity + a UiRenderContext alpha stack applied to sprite/rect
  draws (text bypasses it, stays sharp); chat frame Opacity=0.75 → translucent chat.
- brown sliver: grow the transcript panel up 9px to cover the dropped resize-bar strip.
- scrollbar: real 3-slice thumb (caps 0x06004C60/66 + tiled mid) + tiled track.
- max/min: shifted one button-width left of the scrollbar (dat right-anchors collide).
- system text now green (retail ChatMessageType 5; was yellow).
- word-wrap: transcript lines wrap to the panel width (greedy, ports GlyphList::Recalculate).
- channel menu reworked to retail gmMainChatUI::InitTalkFocusMenu: "Chat" button + a
  TWO-COLUMN popup of the 14 talk-focus items (Squelch, Tell to Selected, Chat to All,
  Tell to Fellows, ...) on a tan panel; channel items set the active outbound channel.

Build + 392 App tests green. Visual confirmation in progress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
This commit is contained in:
Erik 2026-06-16 09:37:40 +02:00
parent 0ec36f6197
commit 1da697ec2a
7 changed files with 329 additions and 126 deletions

View file

@ -22,6 +22,25 @@ public sealed class UiRenderContext
private readonly System.Collections.Generic.List<Vector2> _stack = new();
private Vector2 _current;
// Alpha (opacity) stack — a window pushes its Opacity so its background/sprite
// draws fade (retail's translucent-chat effect). Text draws bypass this (they go
// straight to TextRenderer), so text stays sharp over a translucent background.
private readonly System.Collections.Generic.List<float> _alphaStack = new();
private float _alpha = 1f;
/// <summary>Current cumulative opacity multiplier applied to sprite + rect draws.</summary>
public float AlphaMod => _alpha;
/// <summary>Multiply <paramref name="a"/> into the running opacity. Pair with <see cref="PopAlpha"/>.</summary>
public void PushAlpha(float a) { _alphaStack.Add(_alpha); _alpha *= a; }
public void PopAlpha()
{
if (_alphaStack.Count == 0) return;
_alpha = _alphaStack[^1];
_alphaStack.RemoveAt(_alphaStack.Count - 1);
}
public UiRenderContext(TextRenderer tr, Vector2 screenSize, BitmapFont? defaultFont = null)
{
TextRenderer = tr;
@ -48,15 +67,18 @@ public sealed class UiRenderContext
// ── Pass-through draw helpers (add current translate) ──────────────
public void DrawRect(float x, float y, float w, float h, Vector4 color)
=> TextRenderer.DrawRect(_current.X + x, _current.Y + y, w, h, color);
=> TextRenderer.DrawRect(_current.X + x, _current.Y + y, w, h, ApplyAlpha(color));
public void DrawRectOutline(float x, float y, float w, float h, Vector4 color, float thickness = 1f)
=> TextRenderer.DrawRectOutline(_current.X + x, _current.Y + y, w, h, color, thickness);
=> TextRenderer.DrawRectOutline(_current.X + x, _current.Y + y, w, h, ApplyAlpha(color), thickness);
public void DrawSprite(uint texture, float x, float y, float w, float h,
float u0, float v0, float u1, float v1, Vector4 tint)
=> TextRenderer.DrawSprite(texture,
_current.X + x, _current.Y + y, w, h, u0, v0, u1, v1, tint);
_current.X + x, _current.Y + y, w, h, u0, v0, u1, v1, ApplyAlpha(tint));
/// <summary>Multiply the current window opacity into a draw color's alpha.</summary>
private Vector4 ApplyAlpha(Vector4 c) => _alpha >= 1f ? c : new Vector4(c.X, c.Y, c.Z, c.W * _alpha);
public void DrawString(string text, float x, float y, Vector4 color, BitmapFont? font = null)
{