acdream/docs/research/2026-07-11-retail-inventory-scroll-pseudocode.md
Erik 15aa3b9aff 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>
2026-07-11 10:56:33 +02:00

2.3 KiB

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

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

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.