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:
Erik 2026-07-11 10:56:33 +02:00
parent ea72c395c9
commit 15aa3b9aff
15 changed files with 379 additions and 34 deletions

View file

@ -0,0 +1,63 @@
# Retail inventory listbox scrolling pseudocode
## Named-retail anchors
- `UIElement_ListBox::ScrollToY @ 0x0046D550`
- `UIElement_ListBox::InqScrollDelta @ 0x0046DC10`
- `UIElement_ListBox::ScrollToView @ 0x0046E9F0`
- `UIElement_Scrollable::SetScrollableXY @ 0x004740C0`
- `UIElement_ItemList::ItemList_SetParentContainer @ 0x004E49D0`
- `UIElement_ItemList::RefreshList @ 0x004E4C00`
## Scroll model
```text
ScrollToY(pixelY):
SetScrollableXY(currentX, pixelY, false)
InqScrollDelta(vertical, negativeDirection, page):
if page:
delta = viewportHeight
else if rowCount != 0:
delta = min(scrollableHeight / rowCount, viewportHeight)
else:
delta = 0
return negativeDirection ? -delta : delta
```
The stored position is an integer pixel coordinate, not a row index. Scrollbar
thumb movement can therefore stop with a row partially visible. Wheel/arrow line
steps remain one row high because `InqScrollDelta` derives the line delta from
`scrollableHeight / rowCount`.
`ScrollToView(index)` sums preceding row heights and compares that pixel rectangle
against `[scrollY, scrollY + viewportHeight]`; it adjusts only when the item lies
outside the visible pixel interval.
## Refresh behavior
```text
RefreshList():
ItemList_SetParentContainer(parentContainerID, refresh=true, notify=true)
ItemList_SetParentContainer(...):
update capacity
flush item elements
add current contained objects in order
// no ScrollToHome / SetScrollableXY(0) in this path
```
The item-element rebuild does not explicitly home the list. A client-side retained
rebuild must therefore preserve `m_iScrollableY` and clamp it only after the complete
replacement content establishes its final extent.
## acdream port shape
- `UiItemList.DeferLayout` prevents transient one-cell extents from clamping the
retained pixel offset during `InventoryController.Populate`.
- Intersecting edge cells remain visible at their true `rowY - ScrollY` coordinate.
- `UiElement.ClipsChildren` pushes a nested screen-space clip; `UiRenderContext`
crops sprite geometry and matching UVs, so a partial row paints only inside the
contents viewport and cannot receive input outside it.
- The inventory window's maximum height is the available screen height rather than
the former arbitrary 560 px cap. Exact keystone resize bounds remain AP-54.