fix(ui): port retail item drag visuals

Keep retail's underlay-free m_pDragIcon separate from the full cell icon and reveal the authored ghost mesh on physical source cells for the complete drag lifecycle. This removes the backpack backing from the cursor and retires AP-47.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-13 09:14:24 +02:00
parent 3e84027885
commit ace5880fed
14 changed files with 307 additions and 44 deletions

View file

@ -76,6 +76,16 @@ public class DragDropSpineTests
Assert.Equal(32, g.Value.h);
}
[Fact]
public void GetDragGhost_prefersDedicatedUnderlayFreeTexture()
{
var cell = new UiItemSlot { Width = 36, Height = 36 }; // retail bag cell is larger than its icon
cell.SetItem(0x5001u, 0x99u, dragIconTexture: 0x77u);
Assert.Equal((0x77u, 32, 32), cell.GetDragGhost());
Assert.Equal(0x99u, cell.IconTexture); // source cell keeps the full m_pIcon
}
// ── cell drop-target: DragEnter overlay + DropReleased dispatch ──────────
private static (UiItemList list, UiItemSlot cell, SpyHandler h) ListWithHandler()
{
@ -187,6 +197,32 @@ public class DragDropSpineTests
Assert.Null(root.DragSource); // cleaned up
}
[Fact]
public void InventoryDrag_ghostsSourceUntilRelease()
{
var (root, _, cell) = RootWithBoundSlot(0x5001u);
cell.SourceKind = ItemDragSource.Inventory;
root.OnMouseDown(UiMouseButton.Left, 10, 10);
root.OnMouseMove(20, 10);
Assert.True(cell.WaitingVisual);
root.OnMouseUp(UiMouseButton.Left, 600, 500);
Assert.False(cell.WaitingVisual);
}
[Fact]
public void ShortcutDrag_doesNotGhostSource()
{
var (root, _, cell) = RootWithBoundSlot(0x5001u);
cell.SourceKind = ItemDragSource.ShortcutBar;
root.OnMouseDown(UiMouseButton.Left, 10, 10);
root.OnMouseMove(20, 10);
Assert.False(cell.WaitingVisual);
}
// ── Full UiRoot chain: arming + use-vs-drag ─────────────────────────────
// A bound, hit-testable slot inside a list, sized for the hit-test.
private static (UiRoot root, UiItemList list, UiItemSlot cell) RootWithBoundSlot(uint itemId)

View file

@ -12,6 +12,10 @@ public class UiItemSlotTests
public void DefaultEmptySprite_isToolbarBorder()
=> Assert.Equal(0x060074CFu, new UiItemSlot().EmptySprite);
[Fact]
public void DefaultWaitingSprite_isRetailGhostMesh()
=> Assert.Equal(0x0600109Au, new UiItemSlot().WaitingSprite);
[Fact]
public void Empty_whenNoItem()
{