fix(ui): complete retail inventory scroll polish
Preserve pixel scroll offsets across inventory rebuilds, crop partially visible rows with nested geometry/UV clips, and replace the obsolete 560px resize ceiling with available screen height. Keep retail's row-sized wheel step while allowing continuous scrollbar thumb positions. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
ea72c395c9
commit
15aa3b9aff
15 changed files with 379 additions and 34 deletions
|
|
@ -22,6 +22,7 @@ public enum UiItemListFlow
|
|||
public sealed class UiItemList : UiElement
|
||||
{
|
||||
private readonly List<UiItemSlot> _cells = new();
|
||||
private int _layoutDeferralDepth;
|
||||
|
||||
/// <summary>Vertical scroll model for grid mode (clip+scroll). Bound to the gutter
|
||||
/// UiScrollbar by the panel controller. Inert in fill mode (single toolbar cell).</summary>
|
||||
|
|
@ -89,7 +90,19 @@ public sealed class UiItemList : UiElement
|
|||
cell.Anchors = AnchorEdges.None;
|
||||
_cells.Add(cell);
|
||||
AddChild(cell);
|
||||
LayoutCells();
|
||||
if (_layoutDeferralDepth == 0)
|
||||
LayoutCells();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Batch a procedural list rebuild without repeatedly shrinking the scroll
|
||||
/// extents as cells are re-added. The retained pixel offset is clamped once
|
||||
/// against the completed list when the outermost scope ends.
|
||||
/// </summary>
|
||||
public IDisposable DeferLayout()
|
||||
{
|
||||
_layoutDeferralDepth++;
|
||||
return new LayoutDeferral(this);
|
||||
}
|
||||
|
||||
/// <summary>Grid columns. 1 = single column. Ignored in fill mode.</summary>
|
||||
|
|
@ -143,7 +156,7 @@ public sealed class UiItemList : UiElement
|
|||
|
||||
/// <summary>Position every cell per the current mode: fill (CellWidth<=0) sizes the single
|
||||
/// cell to the list; grid (CellWidth>0) tiles cells by <see cref="Flow"/>, offset by the scroll position
|
||||
/// with whole-row vertical clipping (cells fully outside the view are hidden).</summary>
|
||||
/// with pixel viewport clipping (partially visible edge rows are cropped).</summary>
|
||||
internal void LayoutCells()
|
||||
{
|
||||
if (CellWidth <= 0f)
|
||||
|
|
@ -175,12 +188,14 @@ public sealed class UiItemList : UiElement
|
|||
float top = baseY - scrollY;
|
||||
var cell = _cells[i];
|
||||
cell.Left = x; cell.Top = top; cell.Width = CellWidth; cell.Height = CellHeight;
|
||||
// Whole-row vertical clip (no scissor — mirrors UiText.cs:198). A row fully inside
|
||||
// [0, Height] draws; a partially-scrolled row is hidden.
|
||||
cell.Visible = top >= -0.5f && top + CellHeight <= Height + 0.5f;
|
||||
// Retail listboxes retain intersecting edge rows at arbitrary pixel offsets.
|
||||
// The UiElement child-clip traversal crops their visible portion.
|
||||
cell.Visible = top < Height && top + CellHeight > 0f;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ClipsChildren => CellWidth > 0f;
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
foreach (var c in _cells) RemoveChild(c);
|
||||
|
|
@ -204,4 +219,27 @@ public sealed class UiItemList : UiElement
|
|||
// fill mode keeps the single toolbar cell sized to the list; grid mode tiles cells.
|
||||
LayoutCells();
|
||||
}
|
||||
|
||||
private void EndLayoutDeferral()
|
||||
{
|
||||
if (_layoutDeferralDepth <= 0)
|
||||
return;
|
||||
_layoutDeferralDepth--;
|
||||
if (_layoutDeferralDepth == 0)
|
||||
LayoutCells();
|
||||
}
|
||||
|
||||
private sealed class LayoutDeferral : IDisposable
|
||||
{
|
||||
private UiItemList? _owner;
|
||||
|
||||
public LayoutDeferral(UiItemList owner) => _owner = owner;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UiItemList? owner = _owner;
|
||||
_owner = null;
|
||||
owner?.EndLayoutDeferral();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue