feat(ui): complete retail quick-slot input

Port global toolbar use/select/create actions, migrate the collapsed Ctrl-number bindings, and route them through a focused retained-UI controller. Preserve shortcut aliases as aliases across inventory and paperdoll drops with retail's neutral/accept/reject drag states, preventing physical item moves such as unwielding an equipped helmet.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 08:59:49 +02:00
parent 6fcc510d5d
commit e65119f0c6
24 changed files with 650 additions and 69 deletions

View file

@ -591,8 +591,10 @@ public class InventoryControllerTests
objects.AddOrUpdate(new ClientObject { ObjectId = 0xFFFFu });
var ctrl = (IItemListDragHandler)Bind(layout, objects);
Assert.False(ctrl.OnDragOver(containers, containers.GetItem(0)!, Payload(0xFFFFu))); // full bag → red
Assert.True(ctrl.OnDragOver(grid, grid.GetItem(0)!, Payload(0xFFFFu))); // grid → green
Assert.Equal(ItemDragAcceptance.Reject,
ctrl.OnDragOver(containers, containers.GetItem(0)!, Payload(0xFFFFu))); // full bag → red
Assert.Equal(ItemDragAcceptance.Accept,
ctrl.OnDragOver(grid, grid.GetItem(0)!, Payload(0xFFFFu))); // grid → green
}
[Fact]
@ -616,7 +618,38 @@ public class InventoryControllerTests
var ctrl = (IItemListDragHandler)Bind(layout, objects);
// GetContents(0xC) is empty (a closed bag's contents aren't indexed until opened) → not known-full → accept.
Assert.True(ctrl.OnDragOver(containers, containers.GetItem(0)!, Payload(0xFFFFu)));
Assert.Equal(ItemDragAcceptance.Accept,
ctrl.OnDragOver(containers, containers.GetItem(0)!, Payload(0xFFFFu)));
}
[Fact]
public void ShortcutAliasDropOnInventory_removesOnlyAlias_andNeverMovesEquippedItem()
{
var (layout, grid, _, _, _, _, _, _) = BuildLayout();
var objects = new ClientObjectTable();
const uint helmet = 0xD00Du;
objects.AddOrUpdate(new ClientObject
{
ObjectId = helmet,
Type = ItemType.Armor,
ValidLocations = EquipMask.HeadWear,
});
objects.MoveItem(helmet, Player, newSlot: 0, newEquipLocation: EquipMask.HeadWear);
var puts = new List<(uint item, uint container, int placement)>();
var ctrl = (IItemListDragHandler)Bind(layout, objects, puts: puts);
var payload = new ItemDragPayload(
helmet,
ItemDragSource.ShortcutBar,
SourceSlot: 3,
SourceCell: new UiItemSlot());
Assert.Equal(ItemDragAcceptance.None,
ctrl.OnDragOver(grid, grid.GetItem(0)!, payload));
ctrl.HandleDropRelease(grid, grid.GetItem(0)!, payload);
Assert.Empty(puts);
Assert.Equal(Player, objects.Get(helmet)!.ContainerId);
Assert.Equal(EquipMask.HeadWear, objects.Get(helmet)!.CurrentlyEquippedLocation);
}
[Fact]