Hybrid factory mapping ElementInfo.Type to a behavioral widget or the
UiDatElement generic fallback. Type 7 (UIElement_Meter) → UiMeter with
back/front 3-slice ids populated from grandchild image elements; Type 12
(style prototypes / BaseElement stores) → null so the importer skips
them; all other types → UiDatElement. Rect + anchors are set on every
returned widget via ElementReader.ToAnchors.
BuildMeter walks two levels of the element tree: the two Type-3 slice
containers ordered by ReadOrder (back behind, front on top), then within
each container the image children that carry a DirectState ("" key)
ordered by X for left-cap/center-tile/right-cap. The expand-detail
overlay (present in the front container with only named ShowDetail/
HideDetail states and no "" entry) is excluded by the TryGetValue("")
filter automatically — no name-matching needed.
Fill/Label providers are intentionally NOT set here; Task 6
(VitalsController) binds them to live stat data.
5 TDD tests: Type7→UiMeter, UnknownType→UiDatElement, Type12→null,
rect+anchors propagation, and meter slice extraction with overlay exclusion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
4.7 KiB
C#
112 lines
4.7 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
public class DatWidgetFactoryTests
|
|
{
|
|
private static (uint, int, int) NoTex(uint _) => (0, 0, 0);
|
|
|
|
// ── Test 1: Type 7 → UiMeter ─────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Type7_Meter_MakesUiMeter()
|
|
{
|
|
var e = DatWidgetFactory.Create(new ElementInfo { Type = 7, Width = 150, Height = 16 }, NoTex, null);
|
|
Assert.IsType<UiMeter>(e);
|
|
}
|
|
|
|
// ── Test 2: Unknown type → UiDatElement fallback ─────────────────────────
|
|
|
|
[Fact]
|
|
public void UnknownType_FallsBackToGeneric()
|
|
{
|
|
var e = DatWidgetFactory.Create(new ElementInfo { Type = 999 }, NoTex, null);
|
|
Assert.IsType<UiDatElement>(e);
|
|
}
|
|
|
|
// ── Test 3: Type 12 → null (style prototype, never rendered) ─────────────
|
|
|
|
[Fact]
|
|
public void Type12_StylePrototype_ReturnsNull()
|
|
{
|
|
var e = DatWidgetFactory.Create(new ElementInfo { Type = 12 }, NoTex, null);
|
|
Assert.Null(e);
|
|
}
|
|
|
|
// ── Test 4: Rect + anchors set from ElementInfo ───────────────────────────
|
|
|
|
/// <summary>
|
|
/// A Type-3 element with X=5,Y=21,W=150,H=16, Left=1,Top=1,Right=2 should have
|
|
/// its rect + anchors copied onto the returned widget.
|
|
/// Left=1 (near-pin → AnchorEdges.Left), Top=1 (near-pin → AnchorEdges.Top),
|
|
/// Right=2 (far-pin → AnchorEdges.Right), Bottom=0 (no anchor → neither).
|
|
/// Combined: Left | Top | Right.
|
|
/// </summary>
|
|
[Fact]
|
|
public void RectAndAnchors_SetFromElementInfo()
|
|
{
|
|
var info = new ElementInfo
|
|
{
|
|
Type = 3,
|
|
X = 5, Y = 21,
|
|
Width = 150, Height = 16,
|
|
Left = 1, Top = 1,
|
|
Right = 2, Bottom = 0,
|
|
};
|
|
var e = DatWidgetFactory.Create(info, NoTex, null)!;
|
|
Assert.Equal(5f, e.Left);
|
|
Assert.Equal(21f, e.Top);
|
|
Assert.Equal(150f, e.Width);
|
|
Assert.Equal(16f, e.Height);
|
|
Assert.Equal(AnchorEdges.Left | AnchorEdges.Top | AnchorEdges.Right, e.Anchors);
|
|
}
|
|
|
|
// ── Test 5: Meter slice extraction (the important one) ───────────────────
|
|
|
|
/// <summary>
|
|
/// A meter (Type 7) whose two Type-3 containers each carry 3 image children
|
|
/// (ordered by X, bearing a DirectState "" sprite), plus the front container
|
|
/// has a fourth expand-overlay child with ONLY a named "ShowDetail" state —
|
|
/// that overlay must be excluded from the slice count.
|
|
/// </summary>
|
|
[Fact]
|
|
public void MeterSliceExtraction_ReadsGrandchildImageIds_IgnoresOverlay()
|
|
{
|
|
// Slice ids sourced from format doc §11 — real health-bar ids.
|
|
const uint BackL = 0x0600747Eu, BackT = 0x0600747Fu, BackR = 0x06007480u;
|
|
const uint FrontL = 0x06007481u, FrontT = 0x06007482u, FrontR = 0x06007483u;
|
|
const uint OverlayFile = 0x06007490u;
|
|
|
|
// Back container (ReadOrder 0 — drawn first / behind)
|
|
var backChild = new ElementInfo { Type = 3, ReadOrder = 0 };
|
|
backChild.Children.Add(new ElementInfo { X = 0, StateMedia = { [""] = (BackL, 1) } });
|
|
backChild.Children.Add(new ElementInfo { X = 10, StateMedia = { [""] = (BackT, 1) } });
|
|
backChild.Children.Add(new ElementInfo { X = 140, StateMedia = { [""] = (BackR, 1) } });
|
|
|
|
// Front container (ReadOrder 1 — drawn on top)
|
|
var frontChild = new ElementInfo { Type = 3, ReadOrder = 1 };
|
|
frontChild.Children.Add(new ElementInfo { X = 0, StateMedia = { [""] = (FrontL, 1) } });
|
|
frontChild.Children.Add(new ElementInfo { X = 10, StateMedia = { [""] = (FrontT, 1) } });
|
|
frontChild.Children.Add(new ElementInfo { X = 140, StateMedia = { [""] = (FrontR, 1) } });
|
|
// Expand-detail overlay: named state only — NO DirectState "" — must be ignored.
|
|
frontChild.Children.Add(new ElementInfo
|
|
{
|
|
X = 0,
|
|
StateMedia = { ["ShowDetail"] = (OverlayFile, 3) }
|
|
});
|
|
|
|
var meter = new ElementInfo { Type = 7, Width = 150, Height = 16 };
|
|
meter.Children.Add(backChild);
|
|
meter.Children.Add(frontChild);
|
|
|
|
var e = DatWidgetFactory.Create(meter, NoTex, null);
|
|
|
|
var m = Assert.IsType<UiMeter>(e);
|
|
Assert.Equal(BackL, m.BackLeft);
|
|
Assert.Equal(BackT, m.BackTile);
|
|
Assert.Equal(BackR, m.BackRight);
|
|
Assert.Equal(FrontL, m.FrontLeft);
|
|
Assert.Equal(FrontT, m.FrontTile);
|
|
Assert.Equal(FrontR, m.FrontRight);
|
|
}
|
|
}
|