fix(ui): port source-aware toolbar drops
Plan retail shortcut drops from a complete post-lift snapshot so fresh inventory items use cyclic-right displacement while toolbar aliases restore to their vacated slot. Apply local mutations atomically, preserve raw shortcut fields, emit exact Remove/Add ordering, and retire AP-102 after the live ACE gate. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
b5b230c860
commit
3281e0acb4
8 changed files with 462 additions and 23 deletions
|
|
@ -508,26 +508,54 @@ public sealed class ToolbarController : IItemListDragHandler, IRetainedPanelCont
|
|||
/// <inheritdoc/>
|
||||
public void HandleDropRelease(UiItemList targetList, UiItemSlot targetCell, ItemDragPayload payload)
|
||||
{
|
||||
// Retail gmToolbarUI::HandleDropRelease (0x004be7c0) within-bar reorder branch (deep-dive §5):
|
||||
// evict the target's occupant, place the dragged item there, and bump the evicted item into
|
||||
// the (now-vacated) source slot if it's free.
|
||||
int target = targetCell.SlotIndex;
|
||||
ShortcutEntry? evictedEntry = _store.GetEntry(target); // RemoveShortCutInSlotNum(target)
|
||||
uint evicted = evictedEntry?.ObjectId ?? 0u;
|
||||
if (evicted != 0) { _store.Remove(target); _sendRemoveShortcut?.Invoke((uint)target); }
|
||||
ShortcutEntry dragged = (payload.Shortcut ?? new ShortcutEntry(payload.SourceSlot, payload.ObjId, 0u))
|
||||
.WithIndex(target);
|
||||
_store.Set(dragged); // AddShortcut(dragged, target)
|
||||
_sendAddShortcut?.Invoke(dragged);
|
||||
if (evicted != 0 && evicted != payload.ObjId && _store.IsEmpty(payload.SourceSlot))
|
||||
{ // displaced → vacated source slot
|
||||
ShortcutEntry displaced = evictedEntry!.Value.WithIndex(payload.SourceSlot);
|
||||
_store.Set(displaced);
|
||||
_sendAddShortcut?.Invoke(displaced);
|
||||
}
|
||||
EnsureStoreLoadedForMutation();
|
||||
ShortcutDropSource source = payload.SourceKind == ItemDragSource.ShortcutBar
|
||||
? ShortcutDropSource.ShortcutAlias
|
||||
: ShortcutDropSource.FreshItem;
|
||||
ShortcutEntry dragged = payload.Shortcut
|
||||
?? new ShortcutEntry(payload.SourceSlot, payload.ObjId, 0u);
|
||||
ShortcutMutation[] plan = ShortcutDropPlanner.PlanDrop(
|
||||
_store.Snapshot(), source, payload.SourceSlot, targetCell.SlotIndex, dragged);
|
||||
|
||||
ApplyShortcutPlan(plan);
|
||||
Populate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toolbar notice port for <c>gmToolbarUI::RecvNotice_FullMergingItem @ 0x004BE9B0</c>.
|
||||
/// The owning inventory pipeline calls this when ACE confirms a source stack was fully merged.
|
||||
/// </summary>
|
||||
public void ReplaceFullyMergedShortcut(uint oldObjectId, uint newObjectId)
|
||||
{
|
||||
EnsureStoreLoadedForMutation();
|
||||
ShortcutMutation[] plan = ShortcutDropPlanner.PlanFullStackMerge(
|
||||
_store.Snapshot(), oldObjectId, newObjectId);
|
||||
ApplyShortcutPlan(plan);
|
||||
if (plan.Length > 0)
|
||||
Populate();
|
||||
}
|
||||
|
||||
private void ApplyShortcutPlan(IReadOnlyList<ShortcutMutation> plan)
|
||||
{
|
||||
// Apply the already-validated transaction locally first, then reproduce the
|
||||
// planner's exact Remove/Add wire order. Shortcut events have no rejection reply.
|
||||
foreach (var mutation in plan)
|
||||
{
|
||||
if (mutation.Kind == ShortcutMutationKind.Remove)
|
||||
_store.Remove(mutation.Slot);
|
||||
else if (mutation.Entry is { } entry)
|
||||
_store.Set(entry);
|
||||
}
|
||||
|
||||
foreach (var mutation in plan)
|
||||
{
|
||||
if (mutation.Kind == ShortcutMutationKind.Remove)
|
||||
_sendRemoveShortcut?.Invoke((uint)mutation.Slot);
|
||||
else if (mutation.Entry is { } entry)
|
||||
_sendAddShortcut?.Invoke(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue