Rename UiChatView -> UiText (the retail UIElement_Text class, RegisterElementClass(0xc) @ acclient_2013_pseudo_c.txt:115655). Factory changes (DatWidgetFactory.cs): - Remove the Type-12 skip (was: no-media -> null, with-media -> UiDatElement). - Add Type 12 -> BuildText() -> UiText in the switch. - BuildText extracts the element's Direct/Normal sprite as BackgroundSprite so any dat-media the element carried keeps rendering under the text. UiText changes (renamed from UiChatView.cs): - BackgroundColor default: (0,0,0,0.35) -> (0,0,0,0) (transparent). An unbound UiText draws nothing; the controller opts in to the translucent bg. - New BackgroundSprite + SpriteResolve: optional dat state-sprite background drawn UNDER DrawFill+text (faithful UIElement_Text media support). ChatWindowController.cs (Task 5 Step 8): - Transcript property: UiChatView -> UiText. - Bind() now uses layout.FindElement(TranscriptId) as UiText (factory-built) instead of manually constructing + AddChild-ing a new UiChatView. - Sets BackgroundColor = (0,0,0,0.35) on the found widget (retail translucent bg). - Removes the tInfo null-check from the early guard (transcript is factory-built; iInfo lookup kept for the input widget which is still manually constructed). - BuildLines: UiChatView.Line -> UiText.Line throughout. Vitals frozen: the Type-12 vitals number elements are meter children and are never recursed by BuildWidget (the `if (w is not UiMeter)` gate), so they are not built as widgets and keep rendering via UiMeter.Label. Vitals fixture vitals_2100006C.json unchanged; LayoutConformanceTests + VitalsBindingTests green. Tests: - UiChatViewTests.cs -> UiTextTests.cs (class: UiTextTests, all UiChatView.* -> UiText.*) - UiChatViewDatFontTests.cs -> UiTextDatFontTests.cs (same) - DatWidgetFactoryTests: delete Type12_StylePrototype_ReturnsNull + DatWidgetFactory_Type12WithMedia_Renders; add Type12_Text_MakesUiText + DatWidgetFactory_Type12_AlwaysMakesUiText. - LayoutImporterTests: BuildFromInfos_Type12Child_IsSkipped_Type3Present updated to assert IsType<UiText> (element is now in tree, transparent, not skipped). Divergence register: AP-37 amended -- removed the "standalone Type-0 text elements skipped / dat-text widget is Plan 2" clause (now shipped as UiText); kept the meter-collapse clause and the vitals-numbers-via-UiMeter.Label clause. AP-38/AP-39/AD-28 file references updated UiChatView.cs -> UiText.cs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
4.9 KiB
C#
106 lines
4.9 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
/// <summary>
|
|
/// Pure unit tests for <see cref="LayoutImporter.BuildFromInfos"/> — no dats, no GL.
|
|
/// Verifies the tree-builder: widget dispatch, Type-12 skipping, and meter child consumption.
|
|
/// </summary>
|
|
public class LayoutImporterTests
|
|
{
|
|
private static (uint, int, int) NoTex(uint _) => (0, 0, 0);
|
|
|
|
// ── Test 1: Health meter element → UiMeter with correct rect ─────────────
|
|
|
|
/// <summary>
|
|
/// A Type-7 (meter) child element with X=5,Y=5,W=150,H=16 must produce a UiMeter
|
|
/// that is findable by its id, positioned at Left=5, Width=150.
|
|
/// The resolve lambda is a 1-arg Func<uint,(uint,int,int)>.
|
|
/// </summary>
|
|
[Fact]
|
|
public void BuildFromInfos_HealthMeter_IsUiMeterAtRect()
|
|
{
|
|
var root = new ElementInfo { Id = 0x100005F9, Type = 3, Width = 160, Height = 58 };
|
|
var health = new ElementInfo { Id = 0x100000E6, Type = 7, X = 5, Y = 5, Width = 150, Height = 16 };
|
|
|
|
var tree = LayoutImporter.BuildFromInfos(root, new[] { health }, NoTex, null);
|
|
|
|
var found = tree.FindElement(0x100000E6);
|
|
Assert.IsType<UiMeter>(found);
|
|
Assert.Equal(5f, found!.Left);
|
|
Assert.Equal(150f, found.Width);
|
|
}
|
|
|
|
// ── Test 2: Type-12 child builds a UiText; Type-3 sibling is also present ──
|
|
|
|
/// <summary>
|
|
/// A root with two children: one Type-12 UIElement_Text and one Type-3 container.
|
|
/// The Type-12 must appear as a <see cref="UiText"/> in the tree (transparent,
|
|
/// draws nothing until a controller binds its <c>LinesProvider</c>);
|
|
/// the Type-3 must also be present.
|
|
/// </summary>
|
|
[Fact]
|
|
public void BuildFromInfos_Type12Child_IsSkipped_Type3Present()
|
|
{
|
|
var root = new ElementInfo { Id = 0x10000001, Type = 3, Width = 160, Height = 58 };
|
|
var prototype = new ElementInfo { Id = 0x20000001, Type = 12, Width = 0, Height = 0 };
|
|
var container = new ElementInfo { Id = 0x20000002, Type = 3, Width = 100, Height = 20 };
|
|
|
|
var tree = LayoutImporter.BuildFromInfos(root, new[] { prototype, container }, NoTex, null);
|
|
|
|
// Type-12 is now a UiText (transparent, no lines) — present in the tree.
|
|
Assert.IsType<UiText>(tree.FindElement(0x20000001));
|
|
// Type-3 must also be present.
|
|
Assert.NotNull(tree.FindElement(0x20000002));
|
|
}
|
|
|
|
// ── Test 3: Meter consumes its children — child ids not in byId ──────────
|
|
|
|
/// <summary>
|
|
/// A meter (Type 7) whose children are the 3-slice back/front containers.
|
|
/// The meter itself must be findable; its direct children must NOT appear as
|
|
/// separate nodes in the tree (meters own their children, not the generic tree).
|
|
/// </summary>
|
|
[Fact]
|
|
public void BuildFromInfos_MeterWithChildren_MeterPresent_ChildrenNotInTree()
|
|
{
|
|
const uint MeterId = 0x100000E6u;
|
|
const uint BackLayerId = 0x100000E7u;
|
|
const uint FrontLayerId = 0x00000002u;
|
|
|
|
// Build a minimal meter with back + front containers, each with 3 slice children.
|
|
var backContainer = BuildSliceContainer(BackLayerId, ReadOrder: 0,
|
|
l: 0x0600747Eu, t: 0x0600747Fu, r: 0x06007480u);
|
|
var frontContainer = BuildSliceContainer(FrontLayerId, ReadOrder: 1,
|
|
l: 0x06007481u, t: 0x06007482u, r: 0x06007483u);
|
|
|
|
var meter = new ElementInfo { Id = MeterId, Type = 7, Width = 150, Height = 16 };
|
|
meter.Children.Add(backContainer);
|
|
meter.Children.Add(frontContainer);
|
|
|
|
var root = new ElementInfo { Id = 0x100005F9, Type = 3, Width = 160, Height = 58 };
|
|
|
|
var tree = LayoutImporter.BuildFromInfos(root, new[] { meter }, NoTex, null);
|
|
|
|
// The meter widget is present.
|
|
Assert.IsType<UiMeter>(tree.FindElement(MeterId));
|
|
// The meter's dat-children are NOT separate UiElement nodes.
|
|
Assert.Null(tree.FindElement(BackLayerId));
|
|
Assert.Null(tree.FindElement(FrontLayerId));
|
|
// The UiMeter itself has no Ui children (meters consume their children internally).
|
|
var uiMeter = (UiMeter)tree.FindElement(MeterId)!;
|
|
Assert.Empty(uiMeter.Children);
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
|
|
private static ElementInfo BuildSliceContainer(uint id, uint ReadOrder, uint l, uint t, uint r)
|
|
{
|
|
var c = new ElementInfo { Id = id, Type = 3, ReadOrder = ReadOrder };
|
|
c.Children.Add(new ElementInfo { X = 0, StateMedia = { [""] = (l, 1) } });
|
|
c.Children.Add(new ElementInfo { X = 10, StateMedia = { [""] = (t, 1) } });
|
|
c.Children.Add(new ElementInfo { X = 140, StateMedia = { [""] = (r, 1) } });
|
|
return c;
|
|
}
|
|
}
|