feat(ui): D.2b-B — UiItemList N-cell grid mode
Columns + CellWidth/CellHeight + a row-major CellOffset/LayoutCells pass. CellWidth<=0 keeps the single-cell fill mode (toolbar slot sizes to the list — unchanged); CellWidth>0 tiles cells in a grid. Layout runs on AddItem and per-frame in OnDraw so a list resize reflows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
132bf36daa
commit
4fd4b09f3f
2 changed files with 86 additions and 10 deletions
|
|
@ -49,9 +49,48 @@ public sealed class UiItemList : UiElement
|
||||||
public void AddItem(UiItemSlot cell)
|
public void AddItem(UiItemSlot cell)
|
||||||
{
|
{
|
||||||
cell.SpriteResolve ??= SpriteResolve;
|
cell.SpriteResolve ??= SpriteResolve;
|
||||||
cell.Left = 0; cell.Top = 0; cell.Width = Width; cell.Height = Height;
|
|
||||||
_cells.Add(cell);
|
_cells.Add(cell);
|
||||||
AddChild(cell);
|
AddChild(cell);
|
||||||
|
LayoutCells();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Grid columns (row-major). 1 = single column. Ignored in fill mode.</summary>
|
||||||
|
public int Columns { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>Fixed cell width in grid mode. 0 = "fill the list" — the single cell sizes
|
||||||
|
/// to the whole list (the toolbar single-slot legacy). Set >0 (with CellHeight) for a grid.</summary>
|
||||||
|
public float CellWidth { get; set; }
|
||||||
|
/// <summary>Fixed cell height in grid mode (pairs with CellWidth).</summary>
|
||||||
|
public float CellHeight { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Row-major pixel offset of cell <paramref name="index"/> in a grid of
|
||||||
|
/// <paramref name="columns"/> columns at the given cell pitch.</summary>
|
||||||
|
internal static (float x, float y) CellOffset(int index, int columns, float cellW, float cellH)
|
||||||
|
{
|
||||||
|
int col = index % columns, row = index / columns;
|
||||||
|
return (col * cellW, row * cellH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Position every cell per the current mode: fill (CellWidth<=0) sizes the single
|
||||||
|
/// cell to the list; grid (CellWidth>0) tiles cells row-major at the cell pitch.</summary>
|
||||||
|
private void LayoutCells()
|
||||||
|
{
|
||||||
|
if (CellWidth <= 0f)
|
||||||
|
{
|
||||||
|
if (_cells.Count > 0)
|
||||||
|
{
|
||||||
|
var c = _cells[0];
|
||||||
|
c.Left = 0; c.Top = 0; c.Width = Width; c.Height = Height;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int cols = Columns < 1 ? 1 : Columns;
|
||||||
|
for (int i = 0; i < _cells.Count; i++)
|
||||||
|
{
|
||||||
|
var (x, y) = CellOffset(i, cols, CellWidth, CellHeight);
|
||||||
|
var cell = _cells[i];
|
||||||
|
cell.Left = x; cell.Top = y; cell.Width = CellWidth; cell.Height = CellHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Flush()
|
public void Flush()
|
||||||
|
|
@ -62,14 +101,8 @@ public sealed class UiItemList : UiElement
|
||||||
|
|
||||||
protected override void OnDraw(UiRenderContext ctx)
|
protected override void OnDraw(UiRenderContext ctx)
|
||||||
{
|
{
|
||||||
// The factory sets THIS list's Width/Height AFTER construction, so the cell
|
// The factory sets Width/Height AFTER construction, so re-layout each frame:
|
||||||
// (added in the ctor) starts 0x0. For the single-cell toolbar slot, keep the
|
// fill mode keeps the single toolbar cell sized to the list; grid mode tiles cells.
|
||||||
// cell sized to the list each frame; the cell paints itself in the children
|
LayoutCells();
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
43
tests/AcDream.App.Tests/UI/UiItemListGridTests.cs
Normal file
43
tests/AcDream.App.Tests/UI/UiItemListGridTests.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
using AcDream.App.UI;
|
||||||
|
|
||||||
|
namespace AcDream.App.Tests.UI;
|
||||||
|
|
||||||
|
public class UiItemListGridTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CellOffset_RowMajor()
|
||||||
|
{
|
||||||
|
Assert.Equal((0f, 0f), UiItemList.CellOffset(0, 3, 36, 36));
|
||||||
|
Assert.Equal((72f, 0f), UiItemList.CellOffset(2, 3, 36, 36));
|
||||||
|
Assert.Equal((36f, 36f), UiItemList.CellOffset(4, 3, 36, 36)); // col 1, row 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GridMode_PositionsCellsInColumns()
|
||||||
|
{
|
||||||
|
var list = new UiItemList { Columns = 3, CellWidth = 36, CellHeight = 36 };
|
||||||
|
list.Flush(); // drop the ctor's default cell
|
||||||
|
for (int i = 0; i < 7; i++) list.AddItem(new UiItemSlot());
|
||||||
|
|
||||||
|
var c4 = list.GetItem(4)!;
|
||||||
|
Assert.Equal(36f, c4.Left);
|
||||||
|
Assert.Equal(36f, c4.Top);
|
||||||
|
Assert.Equal(36f, c4.Width);
|
||||||
|
Assert.Equal(36f, c4.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FillMode_SizesSingleCellToList()
|
||||||
|
{
|
||||||
|
// CellWidth defaults to 0 = "fill the list" (single-cell toolbar legacy).
|
||||||
|
var list = new UiItemList { Width = 36, Height = 36 };
|
||||||
|
list.Flush();
|
||||||
|
list.AddItem(new UiItemSlot());
|
||||||
|
|
||||||
|
var c = list.Cell;
|
||||||
|
Assert.Equal(0f, c.Left);
|
||||||
|
Assert.Equal(0f, c.Top);
|
||||||
|
Assert.Equal(36f, c.Width);
|
||||||
|
Assert.Equal(36f, c.Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue