feat(D.5.1): UiItemList widget + factory branch for class 0x10000031
Ports retail UIElement_ItemList (class 0x10000031) as a behavioral-leaf container that owns its UiItemSlot children procedurally. Single-cell default covers every toolbar slot; N-cell grid is deferred to the inventory phase. OnDraw syncs the cell rect to the list's Width/Height each frame so the cell is sized and hit-testable from the first rendered frame, even though the factory sets rect AFTER construction. Factory: adds `0x10000031u => new UiItemList(resolve)` arm before the fallback, so all 18 toolbar itemlist slots route to UiItemList instead of UiDatElement. Tests: 4 new (IsLeafWidget, StartsWithOneCell, Cell_returnsFirstSlot, Create_buildsUiItemList_forItemListClassId). All 4 pass; full suite green (415 pass / 2 skip in App.Tests; 0 fail total). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
28d5837309
commit
9c8db0d577
4 changed files with 97 additions and 0 deletions
|
|
@ -67,6 +67,7 @@ public static class DatWidgetFactory
|
||||||
7 => BuildMeter(info, resolve, datFont), // UIElement_Meter
|
7 => BuildMeter(info, resolve, datFont), // UIElement_Meter
|
||||||
11 => new UiScrollbar(), // UIElement_Scrollbar (reg :124137)
|
11 => new UiScrollbar(), // UIElement_Scrollbar (reg :124137)
|
||||||
12 => BuildText(info, resolve), // UIElement_Text (reg :115655)
|
12 => BuildText(info, resolve), // UIElement_Text (reg :115655)
|
||||||
|
0x10000031u => new UiItemList(resolve), // UIElement_ItemList — toolbar/inventory/paperdoll slots
|
||||||
_ => new UiDatElement(info, resolve), // generic fallback (incl. Type 3 chrome/containers)
|
_ => new UiDatElement(info, resolve), // generic fallback (incl. Type 3 chrome/containers)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
61
src/AcDream.App/UI/UiItemList.cs
Normal file
61
src/AcDream.App/UI/UiItemList.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AcDream.App.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A container of item cells (port of retail UIElement_ItemList, class 0x10000031).
|
||||||
|
/// Behavioral LEAF: it creates/owns its UiItemSlot children procedurally, so the
|
||||||
|
/// LayoutImporter must NOT build dat children. The toolbar uses single-cell
|
||||||
|
/// instances (one slot); the inventory phase will grow this to an N-cell grid.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class UiItemList : UiElement
|
||||||
|
{
|
||||||
|
private readonly List<UiItemSlot> _cells = new();
|
||||||
|
|
||||||
|
public UiItemList(Func<uint, (uint tex, int w, int h)>? spriteResolve = null)
|
||||||
|
{
|
||||||
|
SpriteResolve = spriteResolve;
|
||||||
|
// Single-cell default: every toolbar slot always shows one cell (empty or filled).
|
||||||
|
AddItem(new UiItemSlot { SpriteResolve = spriteResolve });
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ConsumesDatChildren => true;
|
||||||
|
|
||||||
|
public Func<uint, (uint tex, int w, int h)>? SpriteResolve { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Convenience for single-cell slots (the toolbar): the first cell.</summary>
|
||||||
|
public UiItemSlot Cell => _cells[0];
|
||||||
|
|
||||||
|
public int GetNumUIItems() => _cells.Count;
|
||||||
|
|
||||||
|
public UiItemSlot? GetItem(int index)
|
||||||
|
=> index >= 0 && index < _cells.Count ? _cells[index] : null;
|
||||||
|
|
||||||
|
public void AddItem(UiItemSlot cell)
|
||||||
|
{
|
||||||
|
cell.SpriteResolve ??= SpriteResolve;
|
||||||
|
cell.Left = 0; cell.Top = 0; cell.Width = Width; cell.Height = Height;
|
||||||
|
_cells.Add(cell);
|
||||||
|
AddChild(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
foreach (var c in _cells) RemoveChild(c);
|
||||||
|
_cells.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDraw(UiRenderContext ctx)
|
||||||
|
{
|
||||||
|
// The factory sets THIS list's Width/Height AFTER construction, so the cell
|
||||||
|
// (added in the ctor) starts 0x0. For the single-cell toolbar slot, keep the
|
||||||
|
// cell sized to the list each frame; the cell paints itself in the children
|
||||||
|
// pass that follows. (N-cell grid layout is the inventory phase.)
|
||||||
|
if (_cells.Count > 0)
|
||||||
|
{
|
||||||
|
var cell = _cells[0];
|
||||||
|
cell.Left = 0; cell.Top = 0; cell.Width = Width; cell.Height = Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -126,6 +126,16 @@ public class DatWidgetFactoryTests
|
||||||
Assert.IsType<UiMenu>(e);
|
Assert.IsType<UiMenu>(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Test 7: Type 0x10000031 → UiItemList ────────────────────────────────
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Create_buildsUiItemList_forItemListClassId()
|
||||||
|
{
|
||||||
|
var info = new AcDream.App.UI.Layout.ElementInfo { Id = 0x100001A7u, Type = 0x10000031u, Width = 32, Height = 32 };
|
||||||
|
var w = AcDream.App.UI.Layout.DatWidgetFactory.Create(info, _ => (0u, 0, 0), null);
|
||||||
|
Assert.IsType<AcDream.App.UI.UiItemList>(w);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Test 6: Meter slice extraction (the important one) ───────────────────
|
// ── Test 6: Meter slice extraction (the important one) ───────────────────
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
25
tests/AcDream.App.Tests/UI/UiItemListTests.cs
Normal file
25
tests/AcDream.App.Tests/UI/UiItemListTests.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
using AcDream.App.UI;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace AcDream.App.Tests.UI;
|
||||||
|
|
||||||
|
public class UiItemListTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IsLeafWidget() => Assert.True(new UiItemList().ConsumesDatChildren);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StartsWithOneCell_forSingleCellSlot()
|
||||||
|
{
|
||||||
|
var list = new UiItemList();
|
||||||
|
Assert.Equal(1, list.GetNumUIItems());
|
||||||
|
Assert.NotNull(list.GetItem(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Cell_returnsTheFirstSlot()
|
||||||
|
{
|
||||||
|
var list = new UiItemList();
|
||||||
|
Assert.Same(list.GetItem(0), list.Cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue