feat(D.2b): UiButton (Type 1) — Send + Max/Min as generic buttons (widget-generalization Task 3)
Introduces UiButton: a dedicated dat-widget button that ports UIElement_Button (RegisterElementClass(1,...) @ acclient_2013_pseudo_c.txt:125828). State selection, tiled DrawSprite, and label rendering mirror UiDatElement exactly so the chat Send and Max/Min buttons have zero behavioral change. DatWidgetFactory now maps Type 1 → UiButton (beside Type 7 → UiMeter, Type 11 → UiScrollbar). ChatWindowController's Send and Max/Min bind blocks updated from UiDatElement casts to UiButton casts; ClickThrough=false lines dropped (UiButton is interactive by construction). The old UiPanel.cs UiButton (a plain dev-scaffold rect+text button with no dat sprites) is renamed UiSimpleButton to free the name — no production code instantiated it. Full suite: 402 passed, 2 skipped, 0 failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3593d6623d
commit
805ab5f40b
6 changed files with 154 additions and 6 deletions
111
src/AcDream.App/UI/UiButton.cs
Normal file
111
src/AcDream.App/UI/UiButton.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using AcDream.App.UI.Layout;
|
||||
|
||||
namespace AcDream.App.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Generic dat-widget button — the production replacement for any dat element of
|
||||
/// Type 1 (UIElement_Button, registered via RegisterElementClass(1, UIElement_Button::Create)
|
||||
/// @ acclient_2013_pseudo_c.txt:125828).
|
||||
///
|
||||
/// <para>
|
||||
/// Draws per-state sprite media exactly like <see cref="UiDatElement"/> (same
|
||||
/// <c>ActiveState</c> defaulting, same <c>ActiveMedia()</c> fallback chain, same tiled
|
||||
/// <c>DrawSprite</c> call with UV-repeat so chrome edges tile correctly) plus an
|
||||
/// optional centered text label. The click behavior mirrors <see cref="UiDatElement"/>
|
||||
/// one-for-one so the chat Send and Max/Min buttons that previously bound through
|
||||
/// <c>UiDatElement.OnClick</c> continue to work without behavioral change.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// State selection: picks <see cref="ElementInfo.DefaultStateName"/> if set, then
|
||||
/// "Normal" if the element has a Normal state sprite, then falls back to the unnamed
|
||||
/// DirectState ("" key) — identical to <see cref="UiDatElement"/>.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// Built by <see cref="DatWidgetFactory"/> for Type-1 elements (chat Send 0x10000019,
|
||||
/// Max/Min 0x1000046F). NOT the same as <see cref="UiSimpleButton"/>, which is an
|
||||
/// earlier dev-scaffold widget with no dat sprites.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class UiButton : UiElement
|
||||
{
|
||||
private readonly ElementInfo _info;
|
||||
private readonly Func<uint, (uint tex, int w, int h)> _resolve;
|
||||
|
||||
/// <summary>Optional click handler. Wired by the controller (e.g. chat Submit, ToggleMaximize).</summary>
|
||||
public Action? OnClick { get; set; }
|
||||
|
||||
/// <summary>Optional centered text label drawn over the sprite (e.g. "Send" on a blank gold frame).</summary>
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>Dat font for <see cref="Label"/>. Required for the label to draw.</summary>
|
||||
public UiDatFont? LabelFont { get; set; }
|
||||
|
||||
/// <summary>Label color (default white).</summary>
|
||||
public Vector4 LabelColor { get; set; } = Vector4.One;
|
||||
|
||||
/// <summary>
|
||||
/// Active state name, runtime-settable (e.g. Max/Min toggling Normal ↔ Minimized).
|
||||
/// Matches <see cref="UiDatElement.ActiveState"/>.
|
||||
/// </summary>
|
||||
public string ActiveState { get; set; } = "";
|
||||
|
||||
/// <param name="info">Merged <see cref="ElementInfo"/> for this element.</param>
|
||||
/// <param name="resolve">Dat file-id → (GL texture handle, native px width, native px height).
|
||||
/// Returns (0,0,0) when the texture is not yet uploaded.</param>
|
||||
public UiButton(ElementInfo info, Func<uint, (uint tex, int w, int h)> resolve)
|
||||
{
|
||||
_info = info;
|
||||
_resolve = resolve;
|
||||
ClickThrough = false; // buttons are interactive — opt OUT of click-through
|
||||
|
||||
// State defaulting matches UiDatElement exactly:
|
||||
// DefaultStateName wins; else "Normal" if that state has a sprite; else DirectState ("").
|
||||
if (!string.IsNullOrEmpty(info.DefaultStateName))
|
||||
ActiveState = info.DefaultStateName;
|
||||
else if (info.StateMedia.ContainsKey("Normal"))
|
||||
ActiveState = "Normal";
|
||||
// else ActiveState stays "" (DirectState)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the File id for the current <see cref="ActiveState"/>, falling back to
|
||||
/// the DirectState ("" key) if the named state is absent.
|
||||
/// Returns 0 if neither exists.
|
||||
/// Mirrors <see cref="UiDatElement.ActiveMedia()"/>.
|
||||
/// </summary>
|
||||
private uint ActiveFile()
|
||||
=> _info.StateMedia.TryGetValue(ActiveState, out var m) ? m.File
|
||||
: _info.StateMedia.TryGetValue("", out var d) ? d.File : 0u;
|
||||
|
||||
protected override void OnDraw(UiRenderContext ctx)
|
||||
{
|
||||
uint file = ActiveFile();
|
||||
if (file != 0)
|
||||
{
|
||||
var (tex, tw, th) = _resolve(file);
|
||||
if (tex != 0 && tw != 0 && th != 0)
|
||||
{
|
||||
// Tiled draw — same call shape as UiDatElement.OnDraw (UV-repeat; GL_REPEAT-wrapped
|
||||
// UI texture). Matches ImgTex::TileCSI; no Stretch mode exists.
|
||||
ctx.DrawSprite(tex, 0, 0, Width, Height, 0, 0, Width / tw, Height / th, Vector4.One);
|
||||
}
|
||||
}
|
||||
|
||||
if (Label is { Length: > 0 } label && LabelFont is { } lf)
|
||||
{
|
||||
float tx = (Width - lf.MeasureWidth(label)) * 0.5f;
|
||||
float ty = (Height - lf.LineHeight) * 0.5f;
|
||||
ctx.DrawStringDat(lf, label, tx, ty, LabelColor);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnEvent(in UiEvent e)
|
||||
{
|
||||
if (e.Type == UiEventType.Click && OnClick is not null) { OnClick(); return true; }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue