Import the retail arrow-only spell bar scrollbar from LayoutDesc, preserve authored end-button extents and HideDisabled behavior in the shared retained widget, and bind each favorite list to its sole horizontal pixel-scroll model. Match retail selection exposure for off-screen spells and pin the behavior with real-fixture conformance tests. Document the six-slice world-interaction completion program as the active pre-M4 work order. Co-authored-by: Codex <codex@openai.com>
4.5 KiB
Retail favorite spell-bar overflow pseudocode
This note is the implementation oracle for world-interaction completion Slice
- Addresses refer to the September 2013 EoR named retail client. The exact
element graph and inherited properties were verified against combat LayoutDesc
0x21000073and the existing imported fixturecombat_21000073.json.
Authored element graph
Each of the eight SpellCastSubMenu groups contains:
favorite group
scrollbar 0x100000B5, 685x36
decrement button 0x10000071, 23x36
Normal 0x06004CDC
Rollover 0x06004CDD
increment button 0x10000072, 23x36
Normal 0x06004CDE
Rollover 0x06004CDF
item list 0x100000B6, x=23, y=2, 639x32
scrollbar property 0x77 = increment button 0x10000072
scrollbar property 0x78 = decrement button 0x10000071
scrollbar property 0x79 = HideDisabled true
item-list property 0x71 = horizontal scrollbar 0x100000B5
The spell-bar scrollbar has no authored track or thumb. Its 23-pixel buttons occupy the space immediately outside the 639-pixel item-list viewport.
Initialization and ownership
Retail:
SpellCastSubMenu::Init @ 0x004C5A90UIElement_Scrollable::PostInit @ 0x004744B0UIElement_Scrollable::OnSetAttribute @ 0x00474630
SpellCastSubMenu.Init(parent, groupId, tabId, tabIndex):
group = parent.GetChildRecursive(groupId)
spellItemList = group.GetChildRecursive(0x100000B6)
register this as the item-list drag handler
spellTabElement = parent.GetChildRecursive(tabId)
UIElement_Scrollable.OnSetAttribute(property):
if property id == 0x71:
horizontalScrollbarId = property element id
UIElement_Scrollable.PostInit():
horizontalScrollbar = resolve horizontalScrollbarId
register for scrollbar messages
UpdateScrollbarSize(horizontal=true)
The controller does not synthesize arrow buttons. LayoutDesc owns them; the ordinary scrollable/listbox relationship owns the offset.
Size, disabled state, and visibility
Retail:
UIElement_Scrollable::UpdateScrollbarSize_ @ 0x004741A0UIElement_Scrollbar::SetDisabled @ 0x004703D0UIElement_Scrollbar::SetHideDisabled @ 0x00470400UIElement_Scrollbar::UpdateLayout @ 0x004710D0
UpdateScrollbarSize(horizontal):
content = scrollableWidth
viewport = element.Width
if content <= viewport:
content = viewport
scrollOffsetX = 0
disabled = true
else:
disabled = false
scrollbar.Horizontal = true
scrollbar.Disabled = disabled
scrollbar.Proportion = min(1, viewport / content)
Scrollbar.UpdateLayout():
scrollingArea = own rectangle
position decrement button at the start and subtract its authored width
position increment button at the end and subtract its authored width
update thumb/track geometry, when those children exist
Visible = NOT (Disabled AND HideDisabled)
For the favorite bar, disabled plus HideDisabled removes both arrows when every favorite/empty cell fits. With overflow, the arrows remain visible and the item list clips the horizontally translated cells.
Scroll delta and selection exposure
Retail:
UIElement_Scrollable::HandleScrollbarMessage_ @ 0x00474370UIElement_ListBox::InqScrollDelta @ 0x0046DC10SpellCastSubMenu::SetSelected @ 0x004C5B00
on decrement:
delta = -itemList.InqScrollDelta(horizontal, line)
set horizontal pixel offset += delta
on increment:
delta = +itemList.InqScrollDelta(horizontal, line)
set horizontal pixel offset += delta
ItemList.InqScrollDelta(horizontal, line):
if column count is nonzero:
return min(viewportWidth, scrollableWidth / columnCount)
return 0
SpellCastSubMenu.SetSelected(spellId):
for each item in list order:
item.Selected = item.spellId == spellId
if selected:
endowmentSelected = false
itemList.ScrollToView(itemIndex)
selectedSpellId = spellId
The favorite cells are 32 pixels wide, so a line arrow moves exactly 32 pixels. Programmatic or keyboard selection exposes the selected cell even when it was outside the prior viewport.
Port constraints
- Preserve the imported
0x71/0x77/0x78relationships. - Preserve each authored button's dimension; do not assume the 16-pixel chat scrollbar button size.
- Do not fabricate a track/thumb for this arrow-only control.
- Derive disabled state from the shared model's content and viewport extents.
- Hide disabled controls from both drawing and hit-testing.
- Keep every favorite tab's scroll offset independent.