acdream/src/AcDream.App/UI/UiItemList.cs

67 lines
2.6 KiB
C#

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.
/// Valid only while the list has at least one cell; after <see cref="Flush"/>
/// (the inventory-phase rebuild path) the list is empty until <see cref="AddItem"/>
/// runs, so use <see cref="GetItem"/> there instead.</summary>
/// <exception cref="InvalidOperationException">the list has no cells (e.g. after Flush).</exception>
public UiItemSlot Cell => _cells.Count > 0
? _cells[0]
: throw new InvalidOperationException("UiItemList has no cells; call AddItem first or use GetItem(index).");
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;
}
}
}