# Retail item drag visuals — pseudocode Date: 2026-07-13 Scope: the cursor graphic and source-cell visual while dragging an item from an item list. This is a focused continuation of `2026-06-16-ui-item-slot-icon-dragdrop-spine-deep-dive.md`. ## Retail anchors - `IconData::RenderIcons` @ `0x0058d180` (`named-retail/acclient_2013_pseudo_c.txt:407524`) - `UIElement_ItemList::PrepareDragIcon` @ `0x004e2a50` (`named-retail/acclient_2013_pseudo_c.txt:230765`) - `UIElement_ItemList::ItemList_BeginDrag` @ `0x004e32d0` (`named-retail/acclient_2013_pseudo_c.txt:231350`) - `UIElement_UIItem::SetWaitingState` @ `0x004e11b0` (`named-retail/acclient_2013_pseudo_c.txt:229190`) - `ACCWeenieObject::SetWaitingState` @ `0x0058c0d0` (`named-retail/acclient_2013_pseudo_c.txt:406260`) - `UIElement_UIItem` layout child `m_elem_Icon_Ghosted = 0x10000349`; the shared UIItem catalog `0x21000037` gives it DirectState surface `0x0600109A`. The live DAT catalog and the named retail code agree. The in-tree WorldBuilder reference has no retained UI/item-list implementation to compare for this path. ## Literal pseudocode ```text IconData.RenderIcons(item): destroy old m_pIcon and m_pDragIcon dragSurface = transparent 32 x 32 blit item.baseIcon onto dragSurface using normal blit blit item.customOverlay onto dragSurface using alpha blit replace pure-white dragSurface pixels from the effect-color surface m_pDragIcon = Graphic(dragSurface) cellSurface = transparent 32 x 32 blit typeDefaultUnderlay onto cellSurface using normal blit blit item.customUnderlay onto cellSurface using alpha blit blit dragSurface onto cellSurface using alpha blit m_pIcon = Graphic(cellSurface) ItemList.PrepareDragIcon(cell): clear cell.m_dragIcon image object = GetWeenieObject(cell.itemID) set cell.m_dragIcon image to object.GetDragIcon() # m_pDragIcon, not m_pIcon ItemList.ItemList_BeginDrag(cell): if PrepareDragIcon(cell) failed: return if cell is not selected: SetSelectedObject(cell.itemID, false) if list is not vendor, salvage, or shortcut list: cell.SetWaitingState(true) StartDragAndDrop(cell.m_dragIcon, hotspot = 16,16) UIItem.SetWaitingState(waiting): object.SetWaitingState(waiting) if cell is not unghostable: set m_elem_Icon_Ghosted visible = waiting on drag release/cancel: clear source waiting state hide m_elem_Icon_Ghosted ``` ## Destination feedback and source overlay order `UIElement_ItemList::ItemList_DragOver @ 0x004e3400` distinguishes an ordinary slot insertion from dropping into a container: ```text if target list is a container selector and target cell is occupied by a container and that container has an empty item slot: target.SetDragAcceptState(0x10000046) # ItemSlot_DragOver_DropIn # 0x060011F7 green arrow else if target accepts an ordinary item-list placement: target.SetDragAcceptState(0x10000041) # ItemSlot_DragOver_Accept # 0x060011F9 green circle else: target.SetDragAcceptState(0x10000040) # 0x060011F8 reject ``` Therefore the backpack contents grid uses the green circle; the side-bag column and main-pack container cell use the green drop-in arrow. The selected/open indicators remain visible while `m_elem_Icon_Ghosted` is active, so the procedural leaf draws the waiting mesh before those persistent indicators and keeps drag-accept feedback topmost. ## Port mapping - `IconComposer` caches the underlay-free composite separately and exposes `GetDragIcon`; the ordinary `GetIcon` still layers the two underlays beneath the same cached drag pixels. - `UiItemSlot` stores `IconTexture` and `DragIconTexture` separately. `GetDragGhost` returns the latter, while `OnDraw` keeps the former in the source cell. - `UiRoot` owns the generic source-active lifecycle, including release outside UI and source-subtree removal. It dispatches `DragBegin` before enabling the source-active mesh, matching retail's select-then-waiting order. The inventory, paperdoll, and toolbar handlers select an unselected dragged item synchronously. `UiItemSlot` maps the lifecycle to the retail waiting visual for physical lists; shortcut aliases remain unghosted. - `UiItemSlot.WaitingSprite` is the authored `0x0600109A` mesh, not a procedural tint. ## Conformance coverage - `DragDropSpineTests.GetDragGhost_prefersDedicatedUnderlayFreeTexture` - `DragDropSpineTests.InventoryDrag_ghostsSourceUntilRelease` - `InventoryControllerTests.OnDragLift_selectsItem_butKeepsItUntilServerConfirms` - `PaperdollControllerTests.DragLift_selectsEquippedItem_beforeWaitingVisual` - `DragDropSpineTests.ShortcutDrag_doesNotGhostSource` - `UiItemSlotTests.DefaultWaitingSprite_isRetailGhostMesh` - `InventoryControllerTests.Empty_sprites_and_drop_feedback_are_applied_per_list_role` - Existing `IconComposerTests.TwoStageWithEffect_copiesTilePixelBeforeUnderlay` locks the two-stage composition order.