diff --git a/AetherBags/Extensions/DragDropPayloadExtensions.cs b/AetherBags/Extensions/DragDropPayloadExtensions.cs index a8cd0dd..d1da0e6 100644 --- a/AetherBags/Extensions/DragDropPayloadExtensions.cs +++ b/AetherBags/Extensions/DragDropPayloadExtensions.cs @@ -1,4 +1,4 @@ -using AetherBags.Interop; + using AetherBags.Inventory; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -8,55 +8,8 @@ using Lumina.Text; namespace AetherBags.Extensions; -// TODO: Remove FixedInterface when CS is merged into Dalamud. public static unsafe class DragDropPayloadExtensions { - public static DragDropPayload FromFixedInterface(AtkDragDropInterface* dragDropInterface) - { - // Cast to our manual fixed struct - var fixedInterface = (AtkDragDropInterfaceFixed*)dragDropInterface; - - // Calls Index 12 - var payloadContainer = fixedInterface->GetPayloadContainer(); - - return new DragDropPayload - { - Type = fixedInterface->DragDropType, - ReferenceIndex = fixedInterface->DragDropReferenceIndex, - Int1 = payloadContainer->Int1, - Int2 = payloadContainer->Int2, - Text = new ReadOnlySeString(payloadContainer->Text), - }; - } - - public static void ToFixedInterface(this DragDropPayload payload, AtkDragDropInterface* dragDropInterface, bool writeToPayloadContainer = true) - { - var fixedInterface = (AtkDragDropInterfaceFixed*)dragDropInterface; - - fixedInterface->DragDropType = payload.Type; - fixedInterface->DragDropReferenceIndex = payload.ReferenceIndex; - - if (writeToPayloadContainer) - { - // Calls Index 12 - var payloadContainer = fixedInterface->GetPayloadContainer(); - - payloadContainer->Clear(); - payloadContainer->Int1 = payload.Int1; - payloadContainer->Int2 = payload.Int2; - - if (payload.Text.IsEmpty) - { - payloadContainer->Text.Clear(); - } - else - { - var stringBuilder = new SeStringBuilder().Append(payload.Text); - payloadContainer->Text.SetString(stringBuilder.GetViewAsSpan()); - } - } - } - extension(DragDropPayload payload) { public bool IsValidInventoryPayload => @@ -84,7 +37,7 @@ public static unsafe class DragDropPayloadExtensions if (sourceContainer == 0) return new InventoryLocation(0, 0); - // Retainers have special handling: UI has 5 tabs × 35 slots, data has 7 pages × 25 slots + // Retainers have special handling: UI has 5 tabs × 35 slots, data has 7 pages × 25 slots if (sourceContainer.IsRetainer) { // Container IDs 52-56 = UI tabs 0-4 diff --git a/AetherBags/Interop/AtkDragDropInterfaceFixed.cs b/AetherBags/Interop/AtkDragDropInterfaceFixed.cs deleted file mode 100644 index 34daf17..0000000 --- a/AetherBags/Interop/AtkDragDropInterfaceFixed.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace AetherBags.Interop; - -// Size 0x30 (48) matches the original struct -[StructLayout(LayoutKind.Explicit, Size = 48)] -public unsafe struct AtkDragDropInterfaceFixed -{ - // Offset 0 is the Virtual Table Pointer (void**) - [FieldOffset(0)] public void** VirtualTable; - - // Map specific fields needed for Payload logic - [FieldOffset(36)] public DragDropType DragDropType; - [FieldOffset(40)] public short DragDropReferenceIndex; - - // Helper to get 'this' as a pointer - private AtkDragDropInterfaceFixed* ThisPtr => (AtkDragDropInterfaceFixed*)Unsafe.AsPointer(ref this); - - // [VirtualFunction(1)] - public void GetScreenPosition(float* screenX, float* screenY) - { - var fnPtr = (delegate* unmanaged)VirtualTable[1]; - fnPtr(ThisPtr, screenX, screenY); - } - - // [VirtualFunction(3)] - public AtkComponentNode* GetComponentNode() - { - var fnPtr = (delegate* unmanaged)VirtualTable[3]; - return fnPtr(ThisPtr); - } - - // [VirtualFunction(5)] - public void SetComponentNode(AtkComponentNode* node) - { - var fnPtr = (delegate* unmanaged)VirtualTable[5]; - fnPtr(ThisPtr, node); - } - - // [VirtualFunction(6)] - public AtkResNode* GetActiveNode() - { - var fnPtr = (delegate* unmanaged)VirtualTable[6]; - return fnPtr(ThisPtr); - } - - // [VirtualFunction(8)] - public AtkComponentBase* GetComponent() - { - var fnPtr = (delegate* unmanaged)VirtualTable[8]; - return fnPtr(ThisPtr); - } - - // [VirtualFunction(9)] - public bool HandleMouseUpEvent(AtkEventData.AtkMouseData* mouseData) - { - var fnPtr = (delegate* unmanaged)VirtualTable[9]; - return fnPtr(ThisPtr, mouseData) != 0; - } - - // [VirtualFunction(12)] - public AtkDragDropPayloadContainer* GetPayloadContainer() - { - var fnPtr = (delegate* unmanaged)VirtualTable[12]; - return fnPtr(ThisPtr); - } -} \ No newline at end of file diff --git a/AetherBags/Nodes/DragDropNode.cs b/AetherBags/Nodes/DragDropNode.cs deleted file mode 100644 index f99f3b2..0000000 --- a/AetherBags/Nodes/DragDropNode.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System; -using System.Numerics; -using AetherBags.Interop; -using FFXIVClientStructs.FFXIV.Client.Enums; -using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; -using KamiToolKit.Classes; -using KamiToolKit.Classes.Timelines; -using KamiToolKit.Nodes; - -namespace AetherBags.Nodes; - -public unsafe class DragDropNode : ComponentNode { - - // FIX: Manually expose the pointers that are 'internal' in KamiToolKit - // We access the raw AtkComponentNode* via 'this.ResNode' and cast from there. - private AtkComponentDragDrop* Component => (AtkComponentDragDrop*)Node->Component; - private AtkUldComponentDataDragDrop* Data => (AtkUldComponentDataDragDrop*)Component->UldManager.ComponentData; - - public readonly ImageNode DragDropBackgroundNode; - public readonly IconNode IconNode; - - public DragDropNode() { - SetInternalComponentType(ComponentType.DragDrop); - - DragDropBackgroundNode = new SimpleImageNode { - NodeId = 3, - Size = new Vector2(44.0f, 44.0f), - TexturePath = "ui/uld/DragTargetA.tex", - TextureCoordinates = new Vector2(0.0f, 0.0f), - TextureSize = new Vector2(44.0f, 44.0f), - WrapMode = WrapMode.Tile, - NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, - }; - DragDropBackgroundNode.AttachNode(this); - - IconNode = new IconNode { - NodeId = 2, - Size = new Vector2(44.0f, 48.0f), - NodeFlags = NodeFlags.Visible | NodeFlags.Enabled | NodeFlags.EmitsEvents, - }; - IconNode.AttachNode(this); - - LoadTimelines(); - - Data->Nodes[0] = IconNode.NodeId; - - AcceptedType = DragDropType.Everything; - Payload = new DragDropPayload(); - - // Use the fixed shadow struct for writing initial values if needed, - // though direct field access on the struct usually works for simple fields. - // However, to be safe with the VTable fix, we just set fields directly here - // as they are standard offsets, or use the pointer. - Component->AtkDragDropInterface.DragDropType = DragDropType.Everything; - Component->AtkDragDropInterface.DragDropReferenceIndex = 0; - - InitializeComponentEvents(); - - AddEvent(AtkEventType.DragDropBegin, DragDropBeginHandler); - AddEvent(AtkEventType.DragDropInsert, DragDropInsertHandler); - AddEvent(AtkEventType.DragDropDiscard, DragDropDiscardHandler); - AddEvent(AtkEventType.DragDropClick, DragDropClickHandler); - AddEvent(AtkEventType.DragDropRollOver, DragDropRollOverHandler); - AddEvent(AtkEventType.DragDropRollOut, DragDropRollOutHandler); - } - - private bool IsDragDropEndRegistered { get; set; } - - public Action? OnBegin { get; set; } - public Action? OnEnd { get; set; } - public Action? OnPayloadAccepted { get; set; } - public Action? OnDiscard { get; set; } - public Action? OnClicked { get; set; } - public Action? OnRollOver { get; set; } - public Action? OnRollOut { get; set; } - - public DragDropPayload Payload { get; set; } - - public uint IconId { - get => IconNode.IconId; - set { - IconNode.IconId = value; - IconNode.IsVisible = value != 0; - } - } - - public bool IsIconDisabled { - get => IconNode.IsIconDisabled; - set => IconNode.IsIconDisabled = value; - } - - public int Quantity { - get => int.Parse(Component->GetQuantityText().ToString()); - set => Component->SetQuantity(value); - } - - public string QuantityString { - get => Component->GetQuantityText().ToString(); - set => Component->SetQuantityText(value); - } - - public DragDropType AcceptedType { - get => Component->AcceptedType; - set => Component->AcceptedType = value; - } - - public AtkDragDropInterface.SoundEffectSuppression SoundEffectSuppression { - get => Component->AtkDragDropInterface.DragDropSoundEffectSuppression; - set => Component->AtkDragDropInterface.DragDropSoundEffectSuppression = value; - } - - public bool IsDraggable { - get => !Component->Flags.HasFlag(DragDropFlag.Locked); - set { - if (value) { - Component->Flags &= ~DragDropFlag.Locked; - } - else { - Component->Flags |= DragDropFlag.Locked; - } - } - } - - public bool IsClickable { - get => Component->Flags.HasFlag(DragDropFlag.Clickable); - set { - if (value) { - Component->Flags |= DragDropFlag.Clickable; - } - else { - Component->Flags &= ~DragDropFlag.Clickable; - } - } - } - - private void DragDropBeginHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); - - // FIX: Use extension method to write payload using fixed VTable - Payload.ToFixedInterface(atkEventData->DragDropData.DragDropInterface); - - OnBegin?.Invoke(this); - - if (!IsDragDropEndRegistered) { - AddEvent(AtkEventType.DragDropEnd, DragDropEndHandler); - IsDragDropEndRegistered = true; - } - } - - private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); - - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; - - // FIX: Use extension method to read payload using fixed VTable - var payload = DragDropPayloadExtensions.FromFixedInterface(atkEventData->DragDropData.DragDropInterface); - - Payload.Clear(); - IconId = 0; - - OnPayloadAccepted?.Invoke(this, payload); - } - - private void DragDropDiscardHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); - - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; - - OnDiscard?.Invoke(this); - } - - private void DragDropEndHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); - - // FIX: Cast to shadow struct to call the correct GetPayloadContainer (Index 12) - var fixedInterface = (AtkDragDropInterfaceFixed*)atkEventData->DragDropData.DragDropInterface; - fixedInterface->GetPayloadContainer()->Clear(); - - OnEnd?.Invoke(this); - - if (IsDragDropEndRegistered) { - RemoveEvent(AtkEventType.DragDropEnd, DragDropEndHandler); - IsDragDropEndRegistered = false; - } - } - - private void DragDropClickHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { - atkEvent->SetEventIsHandled(); - - atkEvent->State.StateFlags |= AtkEventStateFlags.HasReturnFlags; - atkEvent->State.ReturnFlags = 1; - - OnClicked?.Invoke(this); - } - - private void DragDropRollOverHandler() - => OnRollOver?.Invoke(this); - - private void DragDropRollOutHandler() - => OnRollOut?.Invoke(this); - - public void Clear() { - Payload.Clear(); - IconId = 0; - } - - public void ShowTooltip(AtkTooltipManager.AtkTooltipType type, ActionKind actionKind) { - if (AtkStage.Instance()->DragDropManager.IsDragging) return; - - // FIX: Explicitly use 'this.ResNode' and cast to (AtkResNode*) to avoid ambiguity with the class name - var addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)this); - if (addon is null) return; - - var tooltipArgs = new AtkTooltipManager.AtkTooltipArgs(); - tooltipArgs.Ctor(); - tooltipArgs.ActionArgs.Id = Payload.Int2; - tooltipArgs.ActionArgs.Kind = (DetailKind)actionKind; - - AtkStage.Instance()->TooltipManager.ShowTooltip( - AtkTooltipManager.AtkTooltipType.Action, - addon->Id, - (AtkResNode*)this, // FIX: Explicit cast here as well - &tooltipArgs); - } - - private void LoadTimelines() { - AddTimeline(new TimelineBuilder() - .BeginFrameSet(1, 59) - .AddLabelPair(1, 10, 1) - .AddLabelPair(11, 19, 2) - .AddLabelPair(20, 29, 3) - .AddLabelPair(30, 39, 7) - .AddLabelPair(40, 49, 6) - .AddLabelPair(50, 59, 4) - .EndFrameSet() - .Build()); - } -} \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs index 0be20cf..652ae77 100644 --- a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs @@ -14,14 +14,13 @@ using KamiToolKit.Classes; using KamiToolKit.Nodes; // TODO: Switch back to CS version when Dalamud Updated -using DragDropFixedNode = AetherBags.Nodes.DragDropNode; namespace AetherBags.Nodes.Inventory; public class InventoryCategoryNode : SimpleComponentNode { private readonly TextNode _categoryNameTextNode; - private readonly HybridDirectionalFlexNode _itemGridNode; + private readonly HybridDirectionalFlexNode _itemGridNode; private const float FallbackItemSize = 46; private const float HeaderHeight = 16; @@ -55,7 +54,7 @@ public class InventoryCategoryNode : SimpleComponentNode _categoryNameTextNode.AddFlags(NodeFlags.EmitsEvents | NodeFlags.HasCollision); _categoryNameTextNode.AttachNode(this); - _itemGridNode = new HybridDirectionalFlexNode + _itemGridNode = new HybridDirectionalFlexNode { Position = new Vector2(0, HeaderHeight), Size = new Vector2(240, 92), diff --git a/AetherBags/Nodes/Inventory/InventoryDragDropNode.cs b/AetherBags/Nodes/Inventory/InventoryDragDropNode.cs index 4e5dfc3..285e68e 100644 --- a/AetherBags/Nodes/Inventory/InventoryDragDropNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryDragDropNode.cs @@ -7,12 +7,10 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; -// TODO: Switch back to CS version when Dalamud Updated -using DragDropFixedNode = AetherBags.Nodes.DragDropNode; namespace AetherBags.Nodes.Inventory; -public class InventoryDragDropNode : DragDropFixedNode +public class InventoryDragDropNode : DragDropNode { private readonly TextNode _quantityTextNode; public unsafe InventoryDragDropNode()