acdream/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs
Erik 1453ff7da2 feat(D.2b): retail 3-slice vital bars + headless mockup verifier
Render each vital bar as a horizontal 3-slice from the real retail
RenderSurface sprites (authoritative ids from the vitals LayoutDesc
0x21000014 via dump-vitals-bars): a fixed-width bevelled left-cap, a
stretched glassy-gradient middle, and a fixed-width right-cap. The
empty back track draws full width; the coloured front fill grows from
the left to the value (the track owns the right end, so the fill omits
its own right-cap). Replaces the flat single-sprite Alphablend overlay
that read as the old UI - this is the bordered gradient look from the
retail screenshot (red HP / gold stamina / blue mana).

UiMeter gains the six 9-slice ids (BackLeft/Tile/Right +
FrontLeft/Tile/Right) and a DrawHBar helper; MarkupDocument parses the
backleft/backtile/backright/frontleft/fronttile/frontright attrs;
vitals.xml carries the 18 per-vital ids. The temporary
ACDREAM_BAR_PROVEOUT component grid is removed.

Adds AcDream.Cli render-vitals-mockup: a headless ImageSharp composite
that assembles the bars with the SAME DrawHBar logic, so the sprite
assembly can be verified by eye (Read the PNG) without launching the
client + server - the fast UI-iteration loop the user asked for.
export-ui-sprite dumps a single RenderSurface to PNG for HTML mockups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 21:40:11 +02:00

78 lines
3.2 KiB
C#

using AcDream.App.UI;
namespace AcDream.App.Tests.UI;
public class MarkupDocumentTests
{
private sealed class FakeBinding
{
public float HealthPercent => 0.5f;
public uint? HealthCurrent => 109;
public uint? HealthMax => 218;
public float? ManaPercent => null;
public uint? ManaCurrent => null;
public uint? ManaMax => null;
}
[Fact]
public void Build_CreatesPanelWithMeterFillLabelAndGeometry()
{
const string xml =
"<panel id=\"acdream.vitals\" x=\"10\" y=\"30\" w=\"220\" h=\"96\" title=\"Vitals\">" +
" <meter id=\"health\" x=\"8\" y=\"24\" w=\"200\" h=\"14\" fill=\"{HealthPercent}\" cur=\"{HealthCurrent}\" max=\"{HealthMax}\" color=\"#FFFF0000\"/>" +
"</panel>";
var panel = MarkupDocument.Build(xml, new FakeBinding(), _ => ((uint)1, 32, 32));
Assert.IsType<UiNineSlicePanel>(panel);
Assert.Equal(10f, panel.Left);
Assert.Equal(220f, panel.Width);
Assert.Equal(2, panel.Children.Count); // title UiLabel + 1 meter
var meter = Assert.IsType<UiMeter>(panel.Children[1]);
Assert.Equal(8f, meter.Left);
Assert.Equal(200f, meter.Width);
Assert.Equal(0.5f, meter.Fill());
Assert.Equal("109/218", meter.Label());
}
[Fact]
public void Build_NullBindingValuesYieldNullFillAndLabel()
{
const string xml =
"<panel id=\"v\" x=\"0\" y=\"0\" w=\"10\" h=\"10\" title=\"V\">" +
" <meter id=\"mana\" x=\"0\" y=\"0\" w=\"10\" h=\"2\" fill=\"{ManaPercent}\" cur=\"{ManaCurrent}\" max=\"{ManaMax}\" color=\"#FF0000FF\"/>" +
"</panel>";
var panel = MarkupDocument.Build(xml, new FakeBinding(), _ => ((uint)1, 32, 32));
var meter = Assert.IsType<UiMeter>(panel.Children[1]);
Assert.Null(meter.Fill());
Assert.Null(meter.Label());
}
[Fact]
public void Build_ResizeAttrX_SetsHorizontalOnly()
{
const string xml = "<panel id=\"v\" x=\"0\" y=\"0\" w=\"100\" h=\"50\" title=\"V\" resize=\"x\"></panel>";
var panel = MarkupDocument.Build(xml, new object(), _ => ((uint)1, 32, 32));
Assert.True(panel.ResizeX);
Assert.False(panel.ResizeY);
}
[Fact]
public void Build_ParsesNineSliceBarSpriteIds()
{
const string xml = "<panel id=\"v\" x=\"0\" y=\"0\" w=\"100\" h=\"50\" title=\"V\">" +
"<meter id=\"h\" x=\"0\" y=\"0\" w=\"100\" h=\"14\" fill=\"{HealthPercent}\" " +
"backleft=\"0x06001141\" backtile=\"0x06001140\" backright=\"0x0600113F\" " +
"frontleft=\"0x06001131\" fronttile=\"0x06001132\" frontright=\"0x06001133\"/>" +
"</panel>";
var panel = MarkupDocument.Build(xml, new FakeBinding(), _ => ((uint)7, 32, 32));
var meter = Assert.IsType<UiMeter>(panel.Children[1]);
Assert.Equal(0x06001141u, meter.BackLeft);
Assert.Equal(0x06001140u, meter.BackTile);
Assert.Equal(0x0600113Fu, meter.BackRight);
Assert.Equal(0x06001131u, meter.FrontLeft);
Assert.Equal(0x06001132u, meter.FrontTile);
Assert.Equal(0x06001133u, meter.FrontRight);
Assert.NotNull(meter.SpriteResolve);
}
}