# Retail favorite spell-bar overflow pseudocode This note is the implementation oracle for world-interaction completion Slice 1. Addresses refer to the September 2013 EoR named retail client. The exact element graph and inherited properties were verified against combat LayoutDesc `0x21000073` and the existing imported fixture `combat_21000073.json`. ## Authored element graph Each of the eight `SpellCastSubMenu` groups contains: ```text favorite group scrollbar 0x100000B5, 685x36 increment reference 0x10000072, authored x=0, 23x36 Normal 0x06004CDE (left-facing arrow) Rollover 0x06004CDF decrement reference 0x10000071, authored x=63, 23x36 Normal 0x06004CDC Rollover 0x06004CDD 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. The property names do not determine their drawn side: `UIElement_Scrollbar::UpdateScrollingArea @ 0x00470AA0` places the 0x77 increment reference at the leading/top-left edge and the 0x78 decrement reference at the trailing/bottom-right edge. For this horizontal fixture that means `0x06004CDE` on the left and `0x06004CDC` on the right. ## Initialization and ownership Retail: - `SpellCastSubMenu::Init @ 0x004C5A90` - `UIElement_Scrollable::PostInit @ 0x004744B0` - `UIElement_Scrollable::OnSetAttribute @ 0x00474630` ```text 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_ @ 0x004741A0` - `UIElement_Scrollbar::SetDisabled @ 0x004703D0` - `UIElement_Scrollbar::SetHideDisabled @ 0x00470400` - `UIElement_Scrollbar::UpdateLayout @ 0x004710D0` ```text 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 property-0x77 button at the start and subtract its authored width position property-0x78 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. The combat LayoutDesc deliberately mixes design extents: its outer combat root is 610 pixels wide, while the magic page's children are authored against an 800-pixel parent. Applying retail `UIElement::UpdateForParentSizeChange @ 0x00462640` through that complete raw-edge chain gives a 439-pixel favorite viewport at the 610-pixel root—the 13-slot short bar seen before this slice. The retail presentation exposes 18 favorite cells: nine shortcut-numbered cells plus nine unnumbered cells. At 32 pixels per cell the target viewport is 576 pixels. Because every horizontal raw-edge stage carries the parent delta one-for-one, solving the imported tree for that viewport yields: ```text outer combat root = 747 px magic page = 737 px (5 px inset on each side) favorite group = 622 px (x=40, followed by Cast) scrollbar = 622 px (23 + 576 + 23) favorite list = 576 px (18 * 32) Cast = x=662, width=75 inside the magic page ``` This is a fixed visible-capacity HUD, not a desktop-wide strip. Stretching it to the display width makes the favorite content fit, which legitimately disables and hides the `HideDisabled` overflow arrows. ## Scroll delta and selection exposure Retail: - `UIElement_Scrollable::HandleScrollbarMessage_ @ 0x00474370` - `UIElement_ListBox::InqScrollDelta @ 0x0046DC10` - `SpellCastSubMenu::SetSelected @ 0x004C5B00` ```text 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. An actual programmatic, keyboard, or pointer selection exposes the selected cell even when it was outside the prior viewport. Passive endowment or object-table refresh does not re-run `SetSelected`; doing so would undo a manual scrollbar offset. ## Port constraints - Preserve the imported `0x71`/`0x77`/`0x78` relationships. - Preserve each authored button's dimension; do not assume the 16-pixel chat scrollbar button size. - Preserve each authored button's Normal, Normal_rollover, and Normal_pressed media through the shared scrollbar presentation path. - 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. - Carry the DAT raw-edge policies through the full imported combat tree and solve the root width for an 18-cell (576-pixel) favorite viewport. - Keep the resulting 747-pixel HUD width fixed across desktop resizes; do not turn the favorite bar into a desktop-wide strip.