Skip to content

Commit ab81da4

Browse files
committed
fix: stop drag-drop from reparenting item icons
* squashed and signed commits - Prevent Gwen's default drop handler from reparenting the dragged icon into the hovered control, which could place the icon into the wrong slot/container Draggable now forwards DragAndDrop_HandleDrop to its SlotItem parent, and the drag package stores the source SlotItem in Package.UserData to make slot handlers deterministic. - updates and code cleanup: DragAndDrop_HandleDrop overrides - hotkeys and bags handling for DragAndDrop_HandleDrop - (bags seriously need a big refactor): replaced direct packet store (buggy) and slider (never been coded properly) for at least, a simple and functional YesNo Prompt that stores whole item stacks from inventory. - HotbarItem case returns false (required so items don't go invisible when placing them to hotbars). - SpellItem to Hotbar case returns false (required so spells don't go invisible when placing them to hotbars). - fixes quantityLabels logic - visually functional bags and bank items when moving them around - Ensures all item drops go through TryDropItem, restoring the safety prompt that asks which item and how many to drop. - restores proper prompt for item drag-and-drop - fixes a 1-frame flash where the source slot icon briefly reappears before disappearing again. * Weylon's Review * chore: spell items
1 parent 4891f3c commit ab81da4

16 files changed

Lines changed: 112 additions & 98 deletions

File tree

Intersect.Client.Core/Entities/Player.cs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -415,15 +415,16 @@ public void TryDropItem(int inventorySlotIndex)
415415
}
416416

417417
var quantity = inventorySlot.Quantity;
418-
var canDropMultiple = quantity > 1;
418+
var maxQuantity = GetQuantityOfItemInInventory(itemDescriptor.Id);
419+
var canDropMultiple = maxQuantity > 1;
419420
var inputType = canDropMultiple ? InputType.NumericSliderInput : InputType.YesNo;
420421
var prompt = canDropMultiple ? Strings.Inventory.DropItemPrompt : Strings.Inventory.DropPrompt;
421422
_ = new InputBox(
422423
title: Strings.Inventory.DropItemTitle,
423424
prompt: prompt.ToString(itemDescriptor.Name),
424425
inputType: inputType,
425426
quantity: quantity,
426-
maximumQuantity: GetQuantityOfItemInInventory(itemDescriptor.Id),
427+
maximumQuantity: maxQuantity,
427428
userData: inventorySlotIndex,
428429
onSubmit: (sender, args) =>
429430
{
@@ -1120,41 +1121,32 @@ public void TryStoreItemInBag(int inventorySlotIndex, int bagSlotIndex)
11201121
}
11211122

11221123
var quantity = inventorySlot.Quantity;
1123-
var maxQuantity = quantity;
1124-
1125-
if (maxQuantity < 2)
1126-
{
1127-
PacketSender.SendStoreBagItem(inventorySlotIndex, 1, bagSlotIndex);
1128-
return;
1129-
}
11301124

11311125
_ = new InputBox(
11321126
title: Strings.Bags.StoreItem,
11331127
prompt: Strings.Bags.StoreItemPrompt.ToString(itemDescriptor.Name),
1134-
inputType: InputType.NumericSliderInput,
1128+
inputType: InputType.YesNo,
11351129
quantity: quantity,
1136-
maximumQuantity: maxQuantity,
1137-
userData: new Tuple<int, int>(inventorySlotIndex, bagSlotIndex),
1130+
userData: new Tuple<int, int, int>(inventorySlotIndex, bagSlotIndex, quantity),
11381131
onSubmit: TryStoreItemInBagOnSubmit
11391132
);
11401133
}
11411134

11421135
private static void TryStoreItemInBagOnSubmit(Base sender, InputSubmissionEventArgs args)
11431136
{
1144-
if (sender is not InputBox { UserData: (int inventorySlotIndex, int bagSlotIndex) })
1137+
if (sender is not InputBox { UserData: (int inventorySlotIndex, int bagSlotIndex, int quantity) })
11451138
{
11461139
return;
11471140
}
11481141

1149-
if (args.Value is not NumericalSubmissionValue submissionValue)
1142+
if (args.Value is not BooleanSubmissionValue submissionValue)
11501143
{
11511144
return;
11521145
}
11531146

1154-
var value = (int)Math.Round(submissionValue.Value);
1155-
if (value > 0)
1147+
if (submissionValue.Value)
11561148
{
1157-
PacketSender.SendStoreBagItem(inventorySlotIndex, value, bagSlotIndex);
1149+
PacketSender.SendStoreBagItem(inventorySlotIndex, quantity, bagSlotIndex);
11581150
}
11591151
}
11601152

@@ -1444,12 +1436,17 @@ public void AddToHotbar(int hotbarSlot, sbyte itemType, int itemSlot)
14441436
PacketSender.SendHotbarUpdate(hotbarSlot, itemType, itemSlot);
14451437
}
14461438

1447-
public void HotbarSwap(int index, int swapIndex)
1439+
public bool HotbarSwap(int index, int swapIndex)
14481440
{
14491441
var itemId = Hotbar[index].ItemOrSpellId;
14501442
var bagId = Hotbar[index].BagId;
14511443
var stats = Hotbar[index].PreferredStatBuffs;
14521444

1445+
if (Hotbar[swapIndex].ItemOrSpellId == itemId)
1446+
{
1447+
return false;
1448+
}
1449+
14531450
Hotbar[index].ItemOrSpellId = Hotbar[swapIndex].ItemOrSpellId;
14541451
Hotbar[index].BagId = Hotbar[swapIndex].BagId;
14551452
Hotbar[index].PreferredStatBuffs = Hotbar[swapIndex].PreferredStatBuffs;
@@ -1459,6 +1456,7 @@ public void HotbarSwap(int index, int swapIndex)
14591456
Hotbar[swapIndex].PreferredStatBuffs = stats;
14601457

14611458
PacketSender.SendHotbarSwap(index, swapIndex);
1459+
return true;
14621460
}
14631461

14641462
// Change the dimension if the player is on a gateway

Intersect.Client.Core/Interface/Game/Bag/BagItem.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ public partial class BagItem : SlotItem
1919
{
2020
// Controls
2121
private readonly Label _quantityLabel;
22-
private readonly BagWindow _bagWindow;
2322

2423
// Context Menu Handling
2524
private readonly MenuItem _withdrawContextItem;
2625

27-
public BagItem(BagWindow bagWindow, Base parent, int index, ContextMenu contextMenu)
26+
public BagItem(Base parent, int index, ContextMenu contextMenu)
2827
: base(parent, nameof(BagItem), index, contextMenu)
2928
{
30-
_bagWindow = bagWindow;
3129
TextureFilename = "bagitem.png";
3230

3331
Icon.HoverEnter += Icon_HoverEnter;
@@ -157,6 +155,16 @@ private void Icon_DoubleClicked(Base sender, MouseButtonState arguments)
157155

158156
public override bool DragAndDrop_HandleDrop(Package package, int x, int y)
159157
{
158+
if (Globals.Me is not { } player)
159+
{
160+
return false;
161+
}
162+
163+
if (Globals.BagSlots is not { Length: > 0 } bagSlots)
164+
{
165+
return false;
166+
}
167+
160168
var targetNode = Interface.FindComponentUnderCursor();
161169

162170
// Find the first parent acceptable in that tree that can accept the package
@@ -166,11 +174,11 @@ public override bool DragAndDrop_HandleDrop(Package package, int x, int y)
166174
{
167175
case BagItem bagItem:
168176
PacketSender.SendMoveBagItems(SlotIndex, bagItem.SlotIndex);
169-
return true;
177+
return bagSlots[bagItem.SlotIndex] is not { Quantity: > 0 };
170178

171179
case InventoryItem inventoryItem:
172-
Globals.Me?.TryRetrieveItemFromBag(SlotIndex, inventoryItem.SlotIndex);
173-
return true;
180+
player.TryRetrieveItemFromBag(SlotIndex, inventoryItem.SlotIndex);
181+
return bagSlots[inventoryItem.SlotIndex] is not { Quantity: > 0 };
174182

175183
default:
176184
targetNode = targetNode.Parent;
@@ -206,7 +214,7 @@ public override void Update()
206214
var bagSlot = bagSlots[SlotIndex];
207215
var descriptor = bagSlot.Descriptor;
208216

209-
_quantityLabel.IsVisibleInParent = !Icon.IsDragging && descriptor.IsStackable && bagSlot.Quantity > 1;
217+
_quantityLabel.IsVisibleInParent = !Icon.IsHidden && descriptor.IsStackable && bagSlot.Quantity > 1;
210218
if (_quantityLabel.IsVisibleInParent)
211219
{
212220
_quantityLabel.Text = Strings.FormatQuantityAbbreviated(bagSlot.Quantity);

Intersect.Client.Core/Interface/Game/Bag/BagWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ private void InitItemContainer()
5858

5959
for (var slotIndex = 0; slotIndex < bagSlots.Length; slotIndex++)
6060
{
61-
Items.Add(new BagItem(this, _slotContainer, slotIndex, _contextMenu));
61+
Items.Add(new BagItem(_slotContainer, slotIndex, _contextMenu));
6262
}
6363

6464
PopulateSlotContainer.Populate(_slotContainer, Items);

Intersect.Client.Core/Interface/Game/Bank/BankItem.cs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,12 @@ public partial class BankItem : SlotItem
2222
{
2323
// Controls
2424
private readonly Label _quantityLabel;
25-
private BankWindow _bankWindow;
2625

2726
// Context Menu Handling
2827
private MenuItem _withdrawContextItem;
2928

30-
public BankItem(BankWindow bankWindow, Base parent, int index, ContextMenu contextMenu) :
31-
base(parent, nameof(BankItem), index, contextMenu)
29+
public BankItem(Base parent, int index, ContextMenu contextMenu) : base(parent, nameof(BankItem), index, contextMenu)
3230
{
33-
_bankWindow = bankWindow;
3431
TextureFilename = "bankitem.png";
3532

3633
Icon.HoverEnter += Icon_HoverEnter;
@@ -117,6 +114,7 @@ private void Icon_HoverEnter(Base? sender, EventArgs? arguments)
117114

118115
if (bankSlots[SlotIndex] is not { Descriptor: not null } or { Quantity: <= 0 })
119116
{
117+
_quantityLabel.IsVisibleInParent = false;
120118
return;
121119
}
122120

@@ -206,29 +204,28 @@ public override bool DragAndDrop_HandleDrop(Package package, int x, int y)
206204
}
207205
}
208206

207+
if (Globals.BankSlots is not { Length: > 0 } bankSlots)
208+
{
209+
return false;
210+
}
211+
209212
var targetNode = Interface.FindComponentUnderCursor();
210213

211214
// Find the first parent acceptable in that tree that can accept the package
212215
while (targetNode != default)
213216
{
217+
if (bankSlots[SlotIndex] is not { Quantity: > 0 } slot)
218+
{
219+
return false;
220+
}
221+
214222
switch (targetNode)
215223
{
216224
case BankItem bankItem:
217225
PacketSender.SendMoveBankItems(SlotIndex, bankItem.SlotIndex);
218-
return true;
226+
return bankSlots[bankItem.SlotIndex] is not { Quantity: > 0 };
219227

220228
case InventoryItem inventoryItem:
221-
222-
if (Globals.BankSlots is not { Length: > 0 } bankSlots)
223-
{
224-
return false;
225-
}
226-
227-
if (bankSlots[SlotIndex] is not { Quantity: > 0 } slot)
228-
{
229-
return false;
230-
}
231-
232229
player.TryRetrieveItemFromBank(
233230
SlotIndex,
234231
inventorySlotIndex: inventoryItem.SlotIndex,
@@ -271,7 +268,7 @@ public override void Update()
271268
var bankSlot = bankSlots[SlotIndex];
272269
var descriptor = bankSlot.Descriptor;
273270

274-
_quantityLabel.IsVisibleInParent = !Icon.IsDragging && descriptor.IsStackable && bankSlot.Quantity > 1;
271+
_quantityLabel.IsVisibleInParent = descriptor.IsStackable && bankSlot.Quantity > 1 && !Icon.IsHidden;
275272
if (_quantityLabel.IsVisibleInParent)
276273
{
277274
_quantityLabel.Text = Strings.FormatQuantityAbbreviated(bankSlot.Quantity);
@@ -295,6 +292,7 @@ public override void Update()
295292
{
296293
Icon.Texture = default;
297294
Icon.IsVisibleInParent = false;
295+
_quantityLabel.IsVisibleInParent = false;
298296
}
299297
}
300298
}

Intersect.Client.Core/Interface/Game/Bank/BankWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private void InitItemContainer()
6767
{
6868
for (var slotIndex = 0; slotIndex < Globals.BankSlotCount; slotIndex++)
6969
{
70-
Items.Add(new BankItem(this, _slotContainer, slotIndex, _contextMenu));
70+
Items.Add(new BankItem(_slotContainer, slotIndex, _contextMenu));
7171
}
7272

7373
PopulateSlotContainer.Populate(_slotContainer, Items);

Intersect.Client.Core/Interface/Game/Crafting/RecipeItem.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Intersect.Client.General;
55
using Intersect.Framework.Core.GameObjects.Crafting;
66
using Intersect.Framework.Core.GameObjects.Items;
7-
using Intersect.GameObjects;
87

98
namespace Intersect.Client.Interface.Game.Crafting;
109

@@ -14,8 +13,6 @@ public partial class RecipeItem
1413

1514
public ImagePanel? Container;
1615

17-
public bool IsDragging;
18-
1916
//Dragging
2017
private bool mCanDrag;
2118

Intersect.Client.Core/Interface/Game/Draggable.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public override bool DragAndDrop_Draggable()
1616

1717
public override bool DragAndDrop_CanAcceptPackage(Package package)
1818
{
19+
// Important: Icon is the topmost hovered control, so it must be allowed to "receive" the drop, BUT: we forward handling to SlotItem.
1920
return true;
2021
}
2122

@@ -27,6 +28,7 @@ public override bool DragAndDrop_CanAcceptPackage(Package package)
2728
DrawControl = this,
2829
Name = Name,
2930
HoldOffset = ToLocal(InputHandler.MousePosition.X, InputHandler.MousePosition.Y),
31+
UserData = Parent, // Expected to be SlotItem (InventoryItem/BankItem/etc)
3032
};
3133
}
3234

@@ -37,6 +39,24 @@ public override void DragAndDrop_StartDragging(Package package, int x, int y)
3739

3840
public override void DragAndDrop_EndDragging(bool success, int x, int y)
3941
{
40-
IsVisibleInParent = true;
42+
IsVisibleInParent = !success;
43+
}
44+
45+
public override bool DragAndDrop_HandleDrop(Package package, int x, int y)
46+
{
47+
// Never allow Base.DragAndDrop_HandleDrop() to run on the icon because it reparents SourceControl (Base.cs)
48+
// Forward drop handling to the slot (or nearest SlotItem ancestor).
49+
var node = Parent;
50+
while (node != null)
51+
{
52+
if (node is SlotItem)
53+
{
54+
return node.DragAndDrop_HandleDrop(package, x, y);
55+
}
56+
57+
node = node.Parent;
58+
}
59+
60+
return false;
4161
}
4262
}

Intersect.Client.Core/Interface/Game/Hotbar/HotbarItem.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,9 @@ public override bool DragAndDrop_HandleDrop(Package package, int x, int y)
217217
if (targetNode is HotbarItem hotbarItem)
218218
{
219219
player.HotbarSwap(SlotIndex, hotbarItem.SlotIndex);
220-
return true;
221-
}
222-
else
223-
{
224-
targetNode = targetNode.Parent;
225220
}
221+
222+
targetNode = targetNode.Parent;
226223
}
227224

228225
// If we've reached the top of the tree, we can't drop here, so cancel drop

0 commit comments

Comments
 (0)