fix(ui): restore retail magic shortcut bar
Port the retail horizontal ItemList empty-slot padding and share UIItem shortcut-number graphics with the status toolbar. Preserve all authored face children on compound DAT buttons so the three-piece Cast control reflows and renders as one complete button. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0527325b25
commit
e3605672bb
12 changed files with 466 additions and 62 deletions
|
|
@ -82,6 +82,10 @@ public sealed class UiItemList : UiElement
|
|||
|
||||
public int GetNumUIItems() => _cells.Count;
|
||||
|
||||
/// <summary>Returns this cell's current retained-list position, matching
|
||||
/// UIElement_ListBox::WhatNum. -1 means the cell is not owned by this list.</summary>
|
||||
public int IndexOf(UiItemSlot cell) => _cells.IndexOf(cell);
|
||||
|
||||
public UiItemSlot? GetItem(int index)
|
||||
=> index >= 0 && index < _cells.Count ? _cells[index] : null;
|
||||
|
||||
|
|
@ -128,6 +132,24 @@ public sealed class UiItemList : UiElement
|
|||
/// <summary>Fixed cell height in grid mode (pairs with CellWidth).</summary>
|
||||
public float CellHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lay cells left-to-right in one row. Retail favorite spell lists use the
|
||||
/// horizontal UpdateEmptySlots branch (row-count attribute 0x5F is -1), so
|
||||
/// the number of visible cells is determined by width rather than the nine
|
||||
/// shortcut-number overlays.
|
||||
/// </summary>
|
||||
public bool SingleRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maintain a tail of empty UIItems that fills the visible horizontal extent.
|
||||
/// Port of UIElement_ItemList::UpdateEmptySlots @ 0x004E3700.
|
||||
/// </summary>
|
||||
public bool FillVisibleEmptySlots { get; set; }
|
||||
|
||||
/// <summary>Creates an empty cell for <see cref="FillVisibleEmptySlots"/>.
|
||||
/// Null uses a plain physical UiItemSlot.</summary>
|
||||
public Func<UiItemSlot>? EmptySlotFactory { get; set; }
|
||||
|
||||
/// <summary>Whole rows needed for <paramref name="cellCount"/> cells in a grid of
|
||||
/// <paramref name="columns"/> columns.</summary>
|
||||
public static int RowCount(int cellCount, int columns)
|
||||
|
|
@ -177,7 +199,11 @@ public sealed class UiItemList : UiElement
|
|||
return;
|
||||
}
|
||||
|
||||
int cols = Columns < 1 ? 1 : Columns;
|
||||
UpdateEmptySlots();
|
||||
|
||||
int cols = SingleRow
|
||||
? Math.Max(1, _cells.Count)
|
||||
: Columns < 1 ? 1 : Columns;
|
||||
int cellH = (int)MathF.Round(CellHeight);
|
||||
|
||||
// Drive the shared scroll model from the current geometry, then re-clamp the offset to
|
||||
|
|
@ -201,6 +227,35 @@ public sealed class UiItemList : UiElement
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's horizontal branch pads to floor(listWidth / cellWidth), and when
|
||||
/// the viewport shrinks removes only empty cells from the tail. Occupied cells
|
||||
/// are never discarded merely because they extend beyond the viewport.
|
||||
/// </summary>
|
||||
private void UpdateEmptySlots()
|
||||
{
|
||||
if (!FillVisibleEmptySlots || !SingleRow || CellWidth <= 0f)
|
||||
return;
|
||||
|
||||
int visibleCellCount = Math.Max(0, (int)MathF.Floor(Width / CellWidth));
|
||||
while (_cells.Count < visibleCellCount)
|
||||
{
|
||||
UiItemSlot cell = EmptySlotFactory?.Invoke() ?? new UiItemSlot();
|
||||
cell.SpriteResolve ??= SpriteResolve;
|
||||
if (_cellEmptySprite != 0) cell.EmptySprite = _cellEmptySprite;
|
||||
cell.Anchors = AnchorEdges.None;
|
||||
_cells.Add(cell);
|
||||
AddChild(cell);
|
||||
}
|
||||
|
||||
while (_cells.Count > visibleCellCount && _cells[^1].IsEmptySlot)
|
||||
{
|
||||
UiItemSlot cell = _cells[^1];
|
||||
_cells.RemoveAt(_cells.Count - 1);
|
||||
RemoveChild(cell);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ClipsChildren => CellWidth > 0f;
|
||||
|
||||
public void Flush()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue