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
|
|
@ -320,6 +320,13 @@ public abstract class UiElement
|
|||
/// </summary>
|
||||
protected virtual void OnDrawOverlay(UiRenderContext ctx) { }
|
||||
|
||||
/// <summary>
|
||||
/// When true, descendant drawing and hit-testing are clipped to this element's
|
||||
/// local bounds. Scrollable listboxes use this so edge rows can remain visible
|
||||
/// at arbitrary pixel offsets without painting or receiving input outside the viewport.
|
||||
/// </summary>
|
||||
protected virtual bool ClipsChildren => false;
|
||||
|
||||
/// <summary>Per-frame tick (animations, timers, caret blink).</summary>
|
||||
protected virtual void OnTick(double deltaSeconds) { }
|
||||
|
||||
|
|
@ -378,11 +385,22 @@ public abstract class UiElement
|
|||
// Children painted back-to-front (lowest ZOrder first).
|
||||
if (_children.Count > 0)
|
||||
{
|
||||
// Avoid LINQ allocation by copying to a temp array and sorting.
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawSelfAndChildren(ctx);
|
||||
bool clipsChildren = ClipsChildren;
|
||||
if (clipsChildren)
|
||||
ctx.PushClip(0f, 0f, Width, Height);
|
||||
try
|
||||
{
|
||||
// Avoid LINQ allocation by copying to a temp array and sorting.
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawSelfAndChildren(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (clipsChildren)
|
||||
ctx.PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
// Foreground pass for this element (e.g. a window frame's border drawn
|
||||
|
|
@ -411,10 +429,21 @@ public abstract class UiElement
|
|||
OnDrawOverlay(ctx);
|
||||
if (_children.Count > 0)
|
||||
{
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawOverlays(ctx);
|
||||
bool clipsChildren = ClipsChildren;
|
||||
if (clipsChildren)
|
||||
ctx.PushClip(0f, 0f, Width, Height);
|
||||
try
|
||||
{
|
||||
var ordered = _children.ToArray();
|
||||
Array.Sort(ordered, static (a, b) => a.ZOrder.CompareTo(b.ZOrder));
|
||||
for (int i = 0; i < ordered.Length; i++)
|
||||
ordered[i].DrawOverlays(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (clipsChildren)
|
||||
ctx.PopClip();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
|
@ -440,6 +469,9 @@ public abstract class UiElement
|
|||
internal UiElement? HitTest(float localX, float localY)
|
||||
{
|
||||
if (!Visible || !Enabled) return null;
|
||||
if (ClipsChildren
|
||||
&& (localX < 0f || localX >= Width || localY < 0f || localY >= Height))
|
||||
return null;
|
||||
|
||||
// Children first, in reverse Z-order (topmost first). ClickThrough means
|
||||
// THIS element is transparent to the pointer — but its children are NOT.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue