From fc12b41f335af96c6d52aafdea0cafdf87a10cea Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Fri, 26 Dec 2025 09:02:06 +0100 Subject: [PATCH 1/5] Add Retainers, need to refactor more --- AetherBags.sln.DotSettings.user | 2 + .../Extensions/InventoryTypeExtensions.cs | 76 ++++++++++++++++++- AetherBags/Helpers/InventoryMoveHelper.cs | 4 + AetherBags/Hooks/InventoryHook.cs | 2 +- .../Nodes/Inventory/InventoryCategoryNode.cs | 27 +++++-- 5 files changed, 102 insertions(+), 9 deletions(-) diff --git a/AetherBags.sln.DotSettings.user b/AetherBags.sln.DotSettings.user index f39de2b..cf7e7c4 100644 --- a/AetherBags.sln.DotSettings.user +++ b/AetherBags.sln.DotSettings.user @@ -2,5 +2,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded + ForceIncluded ForceIncluded \ No newline at end of file diff --git a/AetherBags/Extensions/InventoryTypeExtensions.cs b/AetherBags/Extensions/InventoryTypeExtensions.cs index 4c28d8f..0b8ff14 100644 --- a/AetherBags/Extensions/InventoryTypeExtensions.cs +++ b/AetherBags/Extensions/InventoryTypeExtensions.cs @@ -17,6 +17,11 @@ public static unsafe class InventoryTypeExtensions InventoryType.Inventory2 => 49, InventoryType.Inventory3 => 50, InventoryType.Inventory4 => 51, + InventoryType.RetainerPage1 => 52, + InventoryType.RetainerPage2 => 53, + InventoryType.RetainerPage3 => 54, + InventoryType.RetainerPage4 => 55, + InventoryType.RetainerPage5 => 56, InventoryType.ArmoryMainHand => 57, InventoryType.ArmoryHead => 58, InventoryType.ArmoryBody => 59, @@ -45,6 +50,11 @@ public static unsafe class InventoryTypeExtensions 49 => InventoryType.Inventory2, 50 => InventoryType.Inventory3, 51 => InventoryType.Inventory4, + 52 => InventoryType.RetainerPage1, + 53 => InventoryType.RetainerPage2, + 54 => InventoryType.RetainerPage3, + 55 => InventoryType.RetainerPage4, + 56 => InventoryType.RetainerPage5, 57 => InventoryType.ArmoryMainHand, 58 => InventoryType.ArmoryHead, 59 => InventoryType.ArmoryBody, @@ -85,6 +95,13 @@ public static unsafe class InventoryTypeExtensions InventoryType.SaddleBag2 => ItemOrderModule.Instance()->SaddleBagSorter, InventoryType.PremiumSaddleBag1 => ItemOrderModule.Instance()->PremiumSaddleBagSorter, InventoryType.PremiumSaddleBag2 => ItemOrderModule.Instance()->PremiumSaddleBagSorter, + InventoryType.RetainerPage1 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage2 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage3 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage4 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage5 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage6 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), + InventoryType.RetainerPage7 => ItemOrderModule.Instance()->GetActiveRetainerSorter(), _ => null, }; @@ -94,6 +111,12 @@ public static unsafe class InventoryTypeExtensions InventoryType.Inventory4 => inventoryType.GetInventorySorter->ItemsPerPage * 3, InventoryType.SaddleBag2 => inventoryType.GetInventorySorter->ItemsPerPage, InventoryType.PremiumSaddleBag2 => inventoryType.GetInventorySorter->ItemsPerPage, + InventoryType.RetainerPage2 => inventoryType.GetInventorySorter->ItemsPerPage, + InventoryType.RetainerPage3 => inventoryType.GetInventorySorter->ItemsPerPage * 2, + InventoryType.RetainerPage4 => inventoryType.GetInventorySorter->ItemsPerPage * 3, + InventoryType.RetainerPage5 => inventoryType.GetInventorySorter->ItemsPerPage * 4, + InventoryType.RetainerPage6 => inventoryType.GetInventorySorter->ItemsPerPage * 5, + InventoryType.RetainerPage7 => inventoryType.GetInventorySorter->ItemsPerPage * 6, _ => 0, }; @@ -123,11 +146,21 @@ public static unsafe class InventoryTypeExtensions InventoryType.ArmoryRings or InventoryType.ArmorySoulCrystal; + public bool IsRetainer => inventoryType is + InventoryType.RetainerPage1 or + InventoryType.RetainerPage2 or + InventoryType.RetainerPage3 or + InventoryType.RetainerPage4 or + InventoryType.RetainerPage5 or + InventoryType.RetainerPage6 or + InventoryType.RetainerPage7; + public int ContainerGroup => inventoryType switch { _ when inventoryType.IsMainInventory => 1, _ when inventoryType.IsSaddleBag => 2, _ when inventoryType.IsArmory => 3, + _ when inventoryType.IsRetainer => 4, _ => 0, }; @@ -157,9 +190,10 @@ public static unsafe class InventoryTypeExtensions InventoryType baseType = inventoryType switch { _ when inventoryType.IsMainInventory => InventoryType.Inventory1, - _ when inventoryType.IsSaddleBag => inventoryType is InventoryType. SaddleBag1 or InventoryType.SaddleBag2 - ? InventoryType. SaddleBag1 + _ when inventoryType.IsSaddleBag => inventoryType is InventoryType.SaddleBag1 or InventoryType.SaddleBag2 + ? InventoryType.SaddleBag1 : InventoryType.PremiumSaddleBag1, + _ when inventoryType.IsRetainer => InventoryType.RetainerPage1, _ => inventoryType, }; @@ -168,5 +202,43 @@ public static unsafe class InventoryTypeExtensions return (realContainer, realSlot); } + + public int GetVisualSlotFromReal(int realSlot) + { + var sorter = inventoryType.GetInventorySorter; + if (sorter == null) + return realSlot; + + int startIndex = inventoryType.GetInventoryStartIndex; + long itemCount = sorter->Items.LongCount; + + // Search through the sorter to find which visual index maps to this real slot + for (int visualIdx = 0; visualIdx < itemCount; visualIdx++) + { + var entry = sorter->Items[visualIdx]. Value; + if (entry == null) continue; + + // Calculate what container this entry belongs to + InventoryType baseType = inventoryType switch + { + _ when inventoryType.IsMainInventory => InventoryType. Inventory1, + _ when inventoryType.IsSaddleBag => inventoryType is InventoryType.SaddleBag1 or InventoryType.SaddleBag2 + ? InventoryType. SaddleBag1 + : InventoryType.PremiumSaddleBag1, + _ when inventoryType.IsRetainer => InventoryType.RetainerPage1, + _ => inventoryType, + }; + + InventoryType entryContainer = baseType + entry->Page; + + if (entryContainer == inventoryType && entry->Slot == realSlot) + { + // Found it! Return visual index relative to the container's start + return visualIdx - startIndex; + } + } + + return realSlot; // Fallback + } } } \ No newline at end of file diff --git a/AetherBags/Helpers/InventoryMoveHelper.cs b/AetherBags/Helpers/InventoryMoveHelper.cs index ad15f7c..18ef92a 100644 --- a/AetherBags/Helpers/InventoryMoveHelper.cs +++ b/AetherBags/Helpers/InventoryMoveHelper.cs @@ -10,6 +10,10 @@ public static unsafe class InventoryMoveHelper { public static void MoveItem(InventoryType sourceContainer, ushort sourceSlot, InventoryType destContainer, ushort destSlot) { + Services.Logger.Debug($"[MoveItem] {sourceContainer}@{sourceSlot} -> {destContainer}@{destSlot}"); + InventoryManager.Instance()->MoveItemSlot(sourceContainer, sourceSlot, destContainer, destSlot, true); + System.AddonInventoryWindow.ManualInventoryRefresh(); + return; bool isCrossContainerMove = ! sourceContainer.IsSameContainerGroup(destContainer); if (isCrossContainerMove) diff --git a/AetherBags/Hooks/InventoryHook.cs b/AetherBags/Hooks/InventoryHook.cs index 5e821fc..dd2fa48 100644 --- a/AetherBags/Hooks/InventoryHook.cs +++ b/AetherBags/Hooks/InventoryHook.cs @@ -46,7 +46,7 @@ public sealed unsafe class InventoryHooks : IDisposable InventoryItem* sourceItem = InventoryManager.Instance()->GetInventorySlot(srcType, srcSlot); InventoryItem* destItem = InventoryManager.Instance()->GetInventorySlot(dstType, dstSlot); - Services.Logger.Debug($"[MoveItemSlot] Moving {srcType}@{srcSlot} ID:{sourceItem->ItemId} -> {dstType}@{dstSlot} ID:{destItem->ItemId} Unk: {unk}"); + Services.Logger.Debug($"[MoveItemSlot Hook] Moving {srcType}@{srcSlot} ID:{sourceItem->ItemId} -> {dstType}@{dstSlot} ID:{destItem->ItemId} Unk: {unk}"); return _moveItemSlotHook!.Original(manager, srcType, srcSlot, dstType, dstSlot, unk); } diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs index f8fbd36..af2a4f6 100644 --- a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs @@ -248,6 +248,7 @@ public class InventoryCategoryNode : SimpleComponentNode if (payload.Type != DragDropType.Item && payload.Type != DragDropType.Inventory_Item) return; + Services.Logger.Debug($"[OnPayload] Received payload of type {payload.Type}, Int1={payload.Int1}, Int2={payload.Int2}, RefIndex={payload.ReferenceIndex}, Text={payload.Text}"); var (sourceContainer, sourceSlot) = ResolveSourceFromPayload(payload); if (sourceContainer == 0) @@ -272,21 +273,35 @@ public class InventoryCategoryNode : SimpleComponentNode } int containerId = payload.Int1; - int slotIndex = payload.Int2; + int uiSlot = payload.Int2; InventoryType sourceContainer = InventoryType.GetInventoryTypeFromContainerId(containerId); if (sourceContainer == 0) return (0, 0); - // For main inventory, resolve the real slot via ItemOrderModule - if (sourceContainer.IsMainInventory) + // Retainers have special handling: UI has 5 tabs × 35 slots, data has 7 pages × 25 slots + if (sourceContainer. IsRetainer) { - var (realContainer, realSlot) = sourceContainer.GetRealItemLocation(slotIndex); + // Container IDs 52-56 = UI tabs 0-4 + int uiTabIndex = containerId - 52; + + // Convert to global data index + int globalDataIndex = (uiTabIndex * 35) + uiSlot; + + // Calculate data page and slot + int dataPage = globalDataIndex / 25; + int dataSlot = globalDataIndex % 25; + + InventoryType dataContainer = InventoryType.RetainerPage1 + (uint)dataPage; + + // Now resolve through sorter for the actual storage location + var (realContainer, realSlot) = dataContainer.GetRealItemLocation(dataSlot); return (realContainer, realSlot); } - // For other containers (saddlebags, armory, etc.), use the slot directly - return (sourceContainer, (ushort)slotIndex); + // For non-retainers, use the standard resolution + var (realContainerOther, realSlotOther) = sourceContainer.GetRealItemLocation(uiSlot); + return (realContainerOther, realSlotOther); } } \ No newline at end of file From 9c68149d74d09764f19eb79f2603680d136652dd Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sat, 27 Dec 2025 03:54:47 +0100 Subject: [PATCH 2/5] Fix moving retainers, add InventoryNotificationNode --- AetherBags/Addons/AddonInventoryWindow.cs | 8 + .../Extensions/DragDropPayloadExtensions.cs | 59 ++++++- .../Extensions/InventoryTypeExtensions.cs | 50 +----- AetherBags/Helpers/InventoryMoveHelper.cs | 17 +- AetherBags/Inventory/InventoryLocation.cs | 12 ++ .../Nodes/Inventory/InventoryCategoryNode.cs | 57 +----- .../Inventory/InventoryNotificationNode.cs | 162 ++++++++++++++++++ 7 files changed, 260 insertions(+), 105 deletions(-) create mode 100644 AetherBags/Inventory/InventoryLocation.cs create mode 100644 AetherBags/Nodes/Inventory/InventoryNotificationNode.cs diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index 2cf5597..b32f375 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -69,6 +69,14 @@ public class AddonInventoryWindow : NativeAddon float x = headerX + (headerW - size.X) * 0.5f; float y = headerY + (headerH - size.Y) * 0.5f; + InventoryNotificationNode notificationNode = new InventoryNotificationNode + { + Position = new Vector2(WindowNode!.X - 4f, WindowNode!.Y - 32f), + Size = new Vector2(headerW, 28f), + }; + notificationNode.AttachNode(this); + //notificationNode.NotificationType = InventoryNotificationType.SaddleBag; + _searchInputNode = new TextInputWithHintNode { Position = new Vector2(x, y), diff --git a/AetherBags/Extensions/DragDropPayloadExtensions.cs b/AetherBags/Extensions/DragDropPayloadExtensions.cs index 62024dd..a8cd0dd 100644 --- a/AetherBags/Extensions/DragDropPayloadExtensions.cs +++ b/AetherBags/Extensions/DragDropPayloadExtensions.cs @@ -1,4 +1,6 @@ using AetherBags.Interop; +using AetherBags.Inventory; +using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using Lumina.Text.ReadOnly; @@ -6,7 +8,7 @@ using Lumina.Text; namespace AetherBags.Extensions; -// TODO: Remove this when CS is merged into Dalamud. +// TODO: Remove FixedInterface when CS is merged into Dalamud. public static unsafe class DragDropPayloadExtensions { public static DragDropPayload FromFixedInterface(AtkDragDropInterface* dragDropInterface) @@ -54,4 +56,59 @@ public static unsafe class DragDropPayloadExtensions } } } + + extension(DragDropPayload payload) + { + public bool IsValidInventoryPayload => + payload.Type is DragDropType.Inventory_Item + or DragDropType.Inventory_Crystal + or DragDropType.RemoteInventory_Item + or DragDropType.Item; + + public InventoryLocation InventoryLocation + { + get + { + if (!payload.IsValidInventoryPayload) return default; + + if (payload.Type == DragDropType.Inventory_Item) + { + return new InventoryLocation((InventoryType)payload.Int1, (ushort)payload.Int2); + } + + int containerId = payload.Int1; + int uiSlot = payload.Int2; + + InventoryType sourceContainer = InventoryType.GetInventoryTypeFromContainerId(containerId); + + if (sourceContainer == 0) + return new InventoryLocation(0, 0); + + // 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 + int uiTabIndex = containerId - 52; + + // Convert to global data index + int globalDataIndex = (uiTabIndex * 35) + uiSlot; + + // Calculate data page and slot + int dataPage = globalDataIndex / 25; + int dataSlot = globalDataIndex % 25; + + InventoryType dataContainer = InventoryType.RetainerPage1 + (uint)dataPage; + + // Now resolve through sorter for the actual storage location + var (realContainer, realSlot) = dataContainer.GetRealItemLocation(dataSlot); + return new InventoryLocation(realContainer, realSlot); + } + + // For non-retainers, use the standard resolution + var (container, slot) = sourceContainer.GetRealItemLocation(uiSlot); + return new InventoryLocation(container, slot); + } + } + } + } \ No newline at end of file diff --git a/AetherBags/Extensions/InventoryTypeExtensions.cs b/AetherBags/Extensions/InventoryTypeExtensions.cs index 0b8ff14..df91f0b 100644 --- a/AetherBags/Extensions/InventoryTypeExtensions.cs +++ b/AetherBags/Extensions/InventoryTypeExtensions.cs @@ -1,4 +1,5 @@ using System; +using AetherBags.Inventory; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI.Misc; @@ -17,6 +18,7 @@ public static unsafe class InventoryTypeExtensions InventoryType.Inventory2 => 49, InventoryType.Inventory3 => 50, InventoryType.Inventory4 => 51, + // It's possible that these are actually UI IDs InventoryType.RetainerPage1 => 52, InventoryType.RetainerPage2 => 53, InventoryType.RetainerPage3 => 54, @@ -171,21 +173,21 @@ public static unsafe class InventoryTypeExtensions /// Resolves the real container and slot for this inventory type using ItemOrderModule. /// For sorted inventories, the visual slot differs from the actual storage slot. /// - public (InventoryType Container, ushort Slot) GetRealItemLocation(int visualSlot) + public InventoryLocation GetRealItemLocation(int visualSlot) { var sorter = inventoryType.GetInventorySorter; if (sorter == null) - return (inventoryType, (ushort)visualSlot); + return new InventoryLocation(inventoryType, (ushort)visualSlot); int startIndex = inventoryType.GetInventoryStartIndex; int sorterIndex = startIndex + visualSlot; if (sorterIndex < 0 || sorterIndex >= sorter->Items.LongCount) - return (inventoryType, (ushort)visualSlot); + return new InventoryLocation(inventoryType, (ushort)visualSlot); var entry = sorter->Items[sorterIndex].Value; if (entry == null) - return (inventoryType, (ushort)visualSlot); + return new InventoryLocation(inventoryType, (ushort)visualSlot); InventoryType baseType = inventoryType switch { @@ -200,45 +202,7 @@ public static unsafe class InventoryTypeExtensions InventoryType realContainer = baseType + entry->Page; ushort realSlot = entry->Slot; - return (realContainer, realSlot); - } - - public int GetVisualSlotFromReal(int realSlot) - { - var sorter = inventoryType.GetInventorySorter; - if (sorter == null) - return realSlot; - - int startIndex = inventoryType.GetInventoryStartIndex; - long itemCount = sorter->Items.LongCount; - - // Search through the sorter to find which visual index maps to this real slot - for (int visualIdx = 0; visualIdx < itemCount; visualIdx++) - { - var entry = sorter->Items[visualIdx]. Value; - if (entry == null) continue; - - // Calculate what container this entry belongs to - InventoryType baseType = inventoryType switch - { - _ when inventoryType.IsMainInventory => InventoryType. Inventory1, - _ when inventoryType.IsSaddleBag => inventoryType is InventoryType.SaddleBag1 or InventoryType.SaddleBag2 - ? InventoryType. SaddleBag1 - : InventoryType.PremiumSaddleBag1, - _ when inventoryType.IsRetainer => InventoryType.RetainerPage1, - _ => inventoryType, - }; - - InventoryType entryContainer = baseType + entry->Page; - - if (entryContainer == inventoryType && entry->Slot == realSlot) - { - // Found it! Return visual index relative to the container's start - return visualIdx - startIndex; - } - } - - return realSlot; // Fallback + return new InventoryLocation(realContainer, realSlot); } } } \ No newline at end of file diff --git a/AetherBags/Helpers/InventoryMoveHelper.cs b/AetherBags/Helpers/InventoryMoveHelper.cs index 18ef92a..1763765 100644 --- a/AetherBags/Helpers/InventoryMoveHelper.cs +++ b/AetherBags/Helpers/InventoryMoveHelper.cs @@ -8,24 +8,16 @@ namespace AetherBags. Helpers; public static unsafe class InventoryMoveHelper { + // Requires the visual UI slots instead of actual slots. public static void MoveItem(InventoryType sourceContainer, ushort sourceSlot, InventoryType destContainer, ushort destSlot) { Services.Logger.Debug($"[MoveItem] {sourceContainer}@{sourceSlot} -> {destContainer}@{destSlot}"); InventoryManager.Instance()->MoveItemSlot(sourceContainer, sourceSlot, destContainer, destSlot, true); - System.AddonInventoryWindow.ManualInventoryRefresh(); - return; - bool isCrossContainerMove = ! sourceContainer.IsSameContainerGroup(destContainer); - - if (isCrossContainerMove) - { - MoveItemViaAgent(sourceContainer, sourceSlot, destContainer, destSlot); - } - else - { - InventoryManager.Instance()->MoveItemSlot(sourceContainer, sourceSlot, destContainer, destSlot, true); - } + Services.Framework.DelayTicks(2); + Services.Framework.RunOnFrameworkThread(System.AddonInventoryWindow.ManualInventoryRefresh); } + /* private static void MoveItemViaAgent(InventoryType sourceInventory, ushort sourceSlot, InventoryType destInventory, ushort destSlot) { uint sourceContainerId = sourceInventory.AgentItemContainerId; @@ -53,4 +45,5 @@ public static unsafe class InventoryMoveHelper RaptureAtkModule* atkModule = RaptureAtkModule.Instance(); atkModule->HandleItemMove(retVal, atkValues, 4); } + */ } \ No newline at end of file diff --git a/AetherBags/Inventory/InventoryLocation.cs b/AetherBags/Inventory/InventoryLocation.cs new file mode 100644 index 0000000..13b36d1 --- /dev/null +++ b/AetherBags/Inventory/InventoryLocation.cs @@ -0,0 +1,12 @@ +using FFXIVClientStructs.FFXIV.Client.Game; + +namespace AetherBags.Inventory; + +public readonly record struct InventoryLocation(InventoryType Container, ushort Slot) +{ + public static readonly InventoryLocation Invalid = new(0, 0); + + public bool IsValid => Container != 0; + + public override string ToString() => $"{Container}@{Slot}"; +} \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs index af2a4f6..bd332bb 100644 --- a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs @@ -243,65 +243,24 @@ public class InventoryCategoryNode : SimpleComponentNode }; } - private void OnPayloadAccepted(DragDropNode node, DragDropPayload payload, ItemInfo targetItemInfo) + private void OnPayloadAccepted(DragDropNode _, DragDropPayload payload, ItemInfo targetItemInfo) { - if (payload.Type != DragDropType.Item && payload.Type != DragDropType.Inventory_Item) + Services.Logger.Debug($"[OnPayload] Received payload of type {payload.Type}, Int1={payload.Int1}, Int2={payload.Int2}, RefIndex={payload.ReferenceIndex}, Text={payload.Text}"); + if (!payload.IsValidInventoryPayload) return; - Services.Logger.Debug($"[OnPayload] Received payload of type {payload.Type}, Int1={payload.Int1}, Int2={payload.Int2}, RefIndex={payload.ReferenceIndex}, Text={payload.Text}"); - var (sourceContainer, sourceSlot) = ResolveSourceFromPayload(payload); + InventoryLocation sourceLocation = payload.InventoryLocation; - if (sourceContainer == 0) + if (!sourceLocation.IsValid) { Services.Logger.Warning($"[OnPayload] Could not resolve source from payload"); return; } - InventoryType targetContainer = targetItemInfo.Item.Container; - ushort targetSlot = (ushort)targetItemInfo.Item.Slot; + InventoryLocation targetLocation = new InventoryLocation(targetItemInfo.Item.Container, (ushort)targetItemInfo.Item.Slot); - Services.Logger.Debug($"[OnPayload] Moving {sourceContainer}@{sourceSlot} -> {targetContainer}@{targetSlot}"); + Services.Logger.Debug($"[OnPayload] Moving {sourceLocation.ToString()} -> {targetLocation.ToString()}"); - InventoryMoveHelper.MoveItem(sourceContainer, sourceSlot, targetContainer, targetSlot); - } - - private static (InventoryType Container, ushort Slot) ResolveSourceFromPayload(DragDropPayload payload) - { - if (payload.Type == DragDropType.Inventory_Item) - { - return ((InventoryType)payload.Int1, (ushort)payload.Int2); - } - - int containerId = payload.Int1; - int uiSlot = payload.Int2; - - InventoryType sourceContainer = InventoryType.GetInventoryTypeFromContainerId(containerId); - - if (sourceContainer == 0) - return (0, 0); - - // 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 - int uiTabIndex = containerId - 52; - - // Convert to global data index - int globalDataIndex = (uiTabIndex * 35) + uiSlot; - - // Calculate data page and slot - int dataPage = globalDataIndex / 25; - int dataSlot = globalDataIndex % 25; - - InventoryType dataContainer = InventoryType.RetainerPage1 + (uint)dataPage; - - // Now resolve through sorter for the actual storage location - var (realContainer, realSlot) = dataContainer.GetRealItemLocation(dataSlot); - return (realContainer, realSlot); - } - - // For non-retainers, use the standard resolution - var (realContainerOther, realSlotOther) = sourceContainer.GetRealItemLocation(uiSlot); - return (realContainerOther, realSlotOther); + InventoryMoveHelper.MoveItem(sourceLocation.Container, sourceLocation.Slot, targetLocation.Container, targetLocation.Slot); } } \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs new file mode 100644 index 0000000..739a42b --- /dev/null +++ b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs @@ -0,0 +1,162 @@ +using System.Collections.Generic; +using System.Numerics; +using FFXIVClientStructs.FFXIV.Component.GUI; +using KamiToolKit; +using KamiToolKit.Classes; +using KamiToolKit.Classes.Timelines; +using KamiToolKit.Nodes; +using Lumina.Excel; +using Lumina.Excel.Sheets; +using Lumina.Text; +using Lumina.Text.ReadOnly; + +namespace AetherBags.Nodes.Inventory; + +public sealed class InventoryNotificationNode : SimpleComponentNode +{ + private readonly SimpleNineGridNode glowNode; + private readonly TextNode titleTextNode; + private readonly TextNode messageTextNode; + + private Dictionary notificationCache = null!; + + public InventoryNotificationNode() + { + PopulateNotificationCache(); + + AddTimeline(ParentLabels); + + glowNode = new SimpleNineGridNode { + TexturePath = "ui/uld/Inventory.tex", + TextureSize = new Vector2(56.0f, 56.0f), + TextureCoordinates = new Vector2(88.0f, 0.0f), + TopOffset = 10, + BottomOffset = 10, + LeftOffset = 26, + RightOffset = 26, + }; + glowNode.AttachNode(this); + glowNode.AddTimeline(GlowKeyFrames); + + titleTextNode = new TextNode + { + Position = new Vector2(0, 10f), + FontType = FontType.MiedingerMed, + FontSize = 18, + TextColor = ColorHelper.GetColor(50), + TextOutlineColor = ColorHelper.GetColor(37), + TextFlags = TextFlags.Edge, + AlignmentType = AlignmentType.Center, + }; + titleTextNode.AttachNode(this); + titleTextNode.AddTimeline(TextKeyFrames); + + messageTextNode = new TextNode + { + Position = new Vector2(0, -10f), + FontType = FontType.Axis, + FontSize = 14, + TextColor = ColorHelper.GetColor(50), + TextOutlineColor = ColorHelper.GetColor(37), + TextFlags = TextFlags.Edge, + AlignmentType = AlignmentType.Center, + }; + messageTextNode.AttachNode(this); + messageTextNode.AddTimeline(TextKeyFrames); + + Timeline?.PlayAnimation(17); + } + + protected override void OnSizeChanged() + { + base.OnSizeChanged(); + + glowNode.Size = Size with { Y = 40 }; + titleTextNode.Size = Size with { Y = 20 }; + messageTextNode.Size = Size with { Y = 16 }; + } + + private void PopulateNotificationCache() + { + ExcelSheet addonSheet = Services.DataManager.GetExcelSheet(); + notificationCache = new Dictionary + { + { + InventoryNotificationType.SaddleBag, + new InventoryNotificationInfo(addonSheet.GetRow(891).Text, addonSheet.GetRow(892).Text) + }, + { + InventoryNotificationType.RetainerEntrust, + new InventoryNotificationInfo(addonSheet.GetRow(910).Text, addonSheet.GetRow(3573).Text) + }, + { + InventoryNotificationType.RetainerEquip, + new InventoryNotificationInfo(addonSheet.GetRow(910).Text, addonSheet.GetRow(3585).Text) + } + }; + } + + public InventoryNotificationType NotificationType + { + get; + set + { + field = value; + if (value == InventoryNotificationType.None) + { + titleTextNode.String = string.Empty; + messageTextNode.String = string.Empty; + Timeline?.PlayAnimation(17); // Hide + } + else if (notificationCache.TryGetValue(value, out var texts)) + { + titleTextNode.SeString = texts.Title; + messageTextNode.SeString = texts.Message; + Timeline?.PlayAnimation(101); // Show + } + } + } = InventoryNotificationType.None; + + // Future Zeff, this always goes on a parent + private Timeline ParentLabels => new TimelineBuilder() + .BeginFrameSet(1, 59) + .AddLabel(1, 17, AtkTimelineJumpBehavior.PlayOnce, 0) + .AddLabel(10, 101, AtkTimelineJumpBehavior.Start, 0) + .AddLabel(25, 102, AtkTimelineJumpBehavior.Start, 0) + .AddLabel(59, 0, AtkTimelineJumpBehavior.LoopForever, 102) + .EndFrameSet() + .Build(); + + // Future Zeff, this always goes on a child + private Timeline GlowKeyFrames => new TimelineBuilder().BeginFrameSet(15, 59) + .AddFrame(10, scale: new Vector2(1.4f, 1.0f), alpha: 0, addColor: new Vector3(128, 128, 128)) + .AddFrame(15, scale: new Vector2(1.0f, 1.0f), alpha: 255, addColor: new Vector3(128, 128, 128)) + .AddFrame(21, scale: new Vector2(1.0f, 1.0f), alpha: 255, addColor: new Vector3(0, 0, 0)) + .AddFrame(40, scale: new Vector2(1.0f, 1.0f), alpha: 255, addColor: new Vector3(0, 0, 0)) + .AddFrame(46, scale: new Vector2(1.0f, 1.0f), alpha: 255, addColor: new Vector3(10, 10, 10)) + .AddFrame(59, scale: new Vector2(1.0f, 1.0f), alpha: 255, addColor: new Vector3(0, 0, 0)) + .EndFrameSet() + .Build(); + + // Future Zeff, this always goes on a child + private Timeline TextKeyFrames => new TimelineBuilder().BeginFrameSet(15, 59) + .AddFrame(15, alpha: 0, addColor: new Vector3(128, 128, 128)) + .AddFrame(18, alpha: 255, addColor: new Vector3(64, 64, 64)) + .AddFrame(25, alpha: 255, addColor: new Vector3(0, 0, 0)) + .AddFrame(40, alpha: 255, addColor: new Vector3(0, 0, 0)) + .AddFrame(46, alpha: 255, addColor: new Vector3(64, 64, 64)) + .AddFrame(59, alpha: 255, addColor: new Vector3(0, 0, 0)) + .EndFrameSet() + .Build(); + + + private record InventoryNotificationInfo(ReadOnlySeString Title, ReadOnlySeString Message); +} + +public enum InventoryNotificationType +{ + None, + SaddleBag, + RetainerEntrust, + RetainerEquip +} From 09f8201a7b766170092039209ed0b8706c9fd267 Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sat, 27 Dec 2025 05:45:07 +0100 Subject: [PATCH 3/5] Track InventoryNotifications --- AetherBags.sln.DotSettings.user | 2 + AetherBags/Addons/AddonInventoryWindow.cs | 11 ++- .../Inventory/InventoryNotificationState.cs | 99 +++++++++++++++++++ .../Inventory/InventoryNotificationNode.cs | 50 +++------- AetherBags/Services.cs | 1 + 5 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 AetherBags/Inventory/InventoryNotificationState.cs diff --git a/AetherBags.sln.DotSettings.user b/AetherBags.sln.DotSettings.user index cf7e7c4..6ef2c30 100644 --- a/AetherBags.sln.DotSettings.user +++ b/AetherBags.sln.DotSettings.user @@ -1,5 +1,7 @@  + ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index b32f375..6ab3fd7 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -9,6 +9,7 @@ using AetherBags.Nodes.Inventory; using AetherBags.Nodes.Layout; using Dalamud.Game.Addon.Lifecycle; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; +using Dalamud.Game.Gui; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit; @@ -22,6 +23,7 @@ public class AddonInventoryWindow : NativeAddon private readonly InventoryCategoryHoverCoordinator _hoverCoordinator = new(); private readonly HashSet _hoverSubscribed = new(); + private InventoryNotificationNode _notificationNode = null!; private WrappingGridNode _categoriesNode = null!; private TextInputWithHintNode _searchInputNode = null!; private CircleButtonNode _settingsButtonNode = null!; @@ -69,13 +71,12 @@ public class AddonInventoryWindow : NativeAddon float x = headerX + (headerW - size.X) * 0.5f; float y = headerY + (headerH - size.Y) * 0.5f; - InventoryNotificationNode notificationNode = new InventoryNotificationNode + _notificationNode = new InventoryNotificationNode { Position = new Vector2(WindowNode!.X - 4f, WindowNode!.Y - 32f), Size = new Vector2(headerW, 28f), }; - notificationNode.AttachNode(this); - //notificationNode.NotificationType = InventoryNotificationType.SaddleBag; + _notificationNode.AttachNode(this); _searchInputNode = new TextInputWithHintNode { @@ -113,7 +114,6 @@ public class AddonInventoryWindow : NativeAddon base.OnSetup(addon); } - protected override unsafe void OnUpdate(AtkUnitBase* addon) { if (_refreshQueued) @@ -125,6 +125,9 @@ public class AddonInventoryWindow : NativeAddon RefreshCategoriesCore(doAutosize); } + InventoryNotificationType currentNotificationType = (InventoryNotificationType) AgentInventory.Instance()->OpenTitleId; + if(currentNotificationType != _notificationNode.NotificationType) _notificationNode.NotificationType = currentNotificationType; + base.OnUpdate(addon); } diff --git a/AetherBags/Inventory/InventoryNotificationState.cs b/AetherBags/Inventory/InventoryNotificationState.cs new file mode 100644 index 0000000..065fdf5 --- /dev/null +++ b/AetherBags/Inventory/InventoryNotificationState.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using Lumina.Excel; +using Lumina.Excel.Sheets; +using Lumina.Text.ReadOnly; + +namespace AetherBags.Inventory; + +public class InventoryNotificationState +{ + private readonly Dictionary notificationCache; + + public InventoryNotificationState() + { + var addonSheet = Services.DataManager.GetExcelSheet(); + notificationCache = new Dictionary + { + { InventoryNotificationType.Sell, new InventoryNotificationInfo(addonSheet.GetRow(530).Text, addonSheet.GetRow(3576).Text) }, + { InventoryNotificationType.Trade, new InventoryNotificationInfo(addonSheet.GetRow(531).Text, addonSheet.GetRow(3572).Text) }, + { InventoryNotificationType.Letters, new InventoryNotificationInfo(addonSheet.GetRow(549).Text, addonSheet.GetRow(3575).Text) }, + { InventoryNotificationType.Retainer, new InventoryNotificationInfo(addonSheet.GetRow(532).Text, addonSheet.GetRow(3573).Text) }, + { InventoryNotificationType.RetainerEquip, new InventoryNotificationInfo(addonSheet.GetRow(778).Text, addonSheet.GetRow(3585).Text) }, + { InventoryNotificationType.Equip, new InventoryNotificationInfo(addonSheet.GetRow(538).Text, addonSheet.GetRow(3577).Text) }, + { InventoryNotificationType.Armory, new InventoryNotificationInfo(addonSheet.GetRow(775).Text, addonSheet.GetRow(3578).Text) }, + { InventoryNotificationType.Markets, new InventoryNotificationInfo(addonSheet.GetRow(548).Text, addonSheet.GetRow(3574).Text) }, + { InventoryNotificationType.Trade2, new InventoryNotificationInfo(addonSheet.GetRow(531).Text, addonSheet.GetRow(3572).Text) }, + { InventoryNotificationType.CompanyChest, new InventoryNotificationInfo(addonSheet.GetRow(776).Text, addonSheet.GetRow(3579).Text) }, + { InventoryNotificationType.Exterior, new InventoryNotificationInfo(addonSheet.GetRow(3583).Text, addonSheet.GetRow(3581).Text) }, + { InventoryNotificationType.Interior, new InventoryNotificationInfo(addonSheet.GetRow(3584).Text, addonSheet.GetRow(3582).Text) }, + { InventoryNotificationType.Layout, new InventoryNotificationInfo(addonSheet.GetRow(6237).Text, addonSheet.GetRow(3580).Text) }, + { InventoryNotificationType.Plant, new InventoryNotificationInfo(addonSheet.GetRow(6416).Text, addonSheet.GetRow(6418).Text) }, + { InventoryNotificationType.Fertilize, new InventoryNotificationInfo(addonSheet.GetRow(6417).Text, addonSheet.GetRow(6419).Text) }, + { InventoryNotificationType.Transmutation, new InventoryNotificationInfo(addonSheet.GetRow(3911).Text, addonSheet.GetRow(3901).Text) }, + { InventoryNotificationType.Reward, new InventoryNotificationInfo(addonSheet.GetRow(6503).Text, addonSheet.GetRow(6502).Text) }, + { InventoryNotificationType.Feed, new InventoryNotificationInfo(addonSheet.GetRow(6519).Text, addonSheet.GetRow(6518).Text) }, + { InventoryNotificationType.Charge, new InventoryNotificationInfo(addonSheet.GetRow(8638).Text, addonSheet.GetRow(8637).Text) }, + { InventoryNotificationType.Convert, new InventoryNotificationInfo(addonSheet.GetRow(8647).Text, addonSheet.GetRow(8646).Text) }, + { InventoryNotificationType.Covering, new InventoryNotificationInfo(addonSheet.GetRow(9029).Text, addonSheet.GetRow(9028).Text) }, + { InventoryNotificationType.Feed2, new InventoryNotificationInfo(addonSheet.GetRow(9041).Text, addonSheet.GetRow(9040).Text) }, + { InventoryNotificationType.Manual, new InventoryNotificationInfo(addonSheet.GetRow(9044).Text, addonSheet.GetRow(9043).Text) }, + { InventoryNotificationType.Chocobo, new InventoryNotificationInfo(addonSheet.GetRow(9073).Text, addonSheet.GetRow(9072).Text) }, + { InventoryNotificationType.Outfit, new InventoryNotificationInfo(addonSheet.GetRow(6578).Text, addonSheet.GetRow(6579).Text) }, + { InventoryNotificationType.Outfit2, new InventoryNotificationInfo(addonSheet.GetRow(6578).Text, addonSheet.GetRow(6579).Text) }, + { InventoryNotificationType.Plant2, new InventoryNotificationInfo(addonSheet.GetRow(6416).Text, addonSheet.GetRow(6418).Text) }, + { InventoryNotificationType.Aquarium, new InventoryNotificationInfo(addonSheet.GetRow(6808).Text, addonSheet.GetRow(6807).Text) }, + { InventoryNotificationType.SaddleBag, new InventoryNotificationInfo(addonSheet.GetRow(891).Text, addonSheet.GetRow(892).Text) }, + { InventoryNotificationType.Donate, new InventoryNotificationInfo(addonSheet.GetRow(11595).Text, addonSheet.GetRow(11596).Text) }, + { InventoryNotificationType.Trade3, new InventoryNotificationInfo(addonSheet.GetRow(531).Text, addonSheet.GetRow(3572).Text) }, + { InventoryNotificationType.Trade4, new InventoryNotificationInfo(addonSheet.GetRow(531).Text, addonSheet.GetRow(3572).Text) }, + { InventoryNotificationType.Exterior2, new InventoryNotificationInfo(addonSheet.GetRow(3583).Text, addonSheet.GetRow(3581).Text) }, + { InventoryNotificationType.Interior2, new InventoryNotificationInfo(addonSheet.GetRow(6237).Text, addonSheet.GetRow(3580).Text) }, + }; + } + + public InventoryNotificationInfo? GetNotificationInfo(uint openTitleId) + { + return notificationCache.GetValueOrDefault((InventoryNotificationType)openTitleId); + } + + public record InventoryNotificationInfo(ReadOnlySeString Title, ReadOnlySeString Message); +} + +public enum InventoryNotificationType : uint +{ + None = 0, + Sell = 1, + Trade = 2, + Letters = 3, + Retainer = 4, + RetainerEquip = 5, + Equip = 6, + Armory = 7, + Markets = 8, + Trade2 = 9, + CompanyChest = 10, + Exterior = 11, + Interior = 12, + Layout = 13, + Plant = 14, + Fertilize = 15, + Transmutation = 16, + Reward = 17, + Feed = 18, + Charge = 19, + Convert = 20, + Covering = 21, + Feed2 = 22, + Manual = 23, + Chocobo = 24, + Outfit = 25, + Outfit2 = 26, + Plant2 = 27, + Aquarium = 28, + SaddleBag = 29, + Donate = 30, + Trade3 = 31, + Trade4 = 32, + Exterior2 = 33, + Interior2 = 34 +} \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs index 739a42b..a3d260d 100644 --- a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Numerics; +using AetherBags.Inventory; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit; using KamiToolKit.Classes; @@ -18,12 +19,10 @@ public sealed class InventoryNotificationNode : SimpleComponentNode private readonly TextNode titleTextNode; private readonly TextNode messageTextNode; - private Dictionary notificationCache = null!; + private static readonly InventoryNotificationState NotificationState = new(); public InventoryNotificationNode() { - PopulateNotificationCache(); - AddTimeline(ParentLabels); glowNode = new SimpleNineGridNode { @@ -76,26 +75,6 @@ public sealed class InventoryNotificationNode : SimpleComponentNode messageTextNode.Size = Size with { Y = 16 }; } - private void PopulateNotificationCache() - { - ExcelSheet addonSheet = Services.DataManager.GetExcelSheet(); - notificationCache = new Dictionary - { - { - InventoryNotificationType.SaddleBag, - new InventoryNotificationInfo(addonSheet.GetRow(891).Text, addonSheet.GetRow(892).Text) - }, - { - InventoryNotificationType.RetainerEntrust, - new InventoryNotificationInfo(addonSheet.GetRow(910).Text, addonSheet.GetRow(3573).Text) - }, - { - InventoryNotificationType.RetainerEquip, - new InventoryNotificationInfo(addonSheet.GetRow(910).Text, addonSheet.GetRow(3585).Text) - } - }; - } - public InventoryNotificationType NotificationType { get; @@ -108,11 +87,15 @@ public sealed class InventoryNotificationNode : SimpleComponentNode messageTextNode.String = string.Empty; Timeline?.PlayAnimation(17); // Hide } - else if (notificationCache.TryGetValue(value, out var texts)) + else { - titleTextNode.SeString = texts.Title; - messageTextNode.SeString = texts.Message; - Timeline?.PlayAnimation(101); // Show + var info = NotificationState.GetNotificationInfo((uint)value); + if (info != null) + { + titleTextNode.SeString = info.Title; + messageTextNode.SeString = info.Message; + Timeline?.PlayAnimation(101); // Show + } } } } = InventoryNotificationType.None; @@ -148,15 +131,4 @@ public sealed class InventoryNotificationNode : SimpleComponentNode .AddFrame(59, alpha: 255, addColor: new Vector3(0, 0, 0)) .EndFrameSet() .Build(); - - - private record InventoryNotificationInfo(ReadOnlySeString Title, ReadOnlySeString Message); -} - -public enum InventoryNotificationType -{ - None, - SaddleBag, - RetainerEntrust, - RetainerEquip -} +} \ No newline at end of file diff --git a/AetherBags/Services.cs b/AetherBags/Services.cs index 00eeddf..1569de2 100644 --- a/AetherBags/Services.cs +++ b/AetherBags/Services.cs @@ -13,6 +13,7 @@ public class Services [PluginService] public static IDataManager DataManager { get; set; } = null!; [PluginService] public static IDalamudPluginInterface PluginInterface { get; private set; } = null!; [PluginService] public static IFramework Framework { get; private set; } = null!; + [PluginService] public static IGameGui GameGui { get; private set; } = null!; [PluginService] public static IKeyState KeyState { get; private set; } = null!; [PluginService] public static IPluginLog Logger { get; private set; } = null!; [PluginService] public static INotificationManager NotificationManager { get; private set; } = null!; From 048ac5499464c7c3a66c1ec9ad521655baebf53a Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sat, 27 Dec 2025 08:21:14 +0100 Subject: [PATCH 4/5] Update KTK --- KamiToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KamiToolKit b/KamiToolKit index f16338c..6499c4c 160000 --- a/KamiToolKit +++ b/KamiToolKit @@ -1 +1 @@ -Subproject commit f16338cf173eca3a0fcca389533a8ada1c4ef624 +Subproject commit 6499c4c309a912caf260f8d49e511738ed073209 From ab0692299b4bfbabddd0bfafdbcb3cd7eaee87a4 Mon Sep 17 00:00:00 2001 From: Zeffuro Date: Sat, 27 Dec 2025 09:09:20 +0100 Subject: [PATCH 5/5] Update KTK, fix tooltips --- AetherBags/Nodes/DragDropNode.cs | 22 ------------------- .../Nodes/Input/TextInputWithHintNode.cs | 2 +- .../Nodes/Inventory/InventoryCategoryNode.cs | 17 ++++++++------ KamiToolKit | 2 +- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/AetherBags/Nodes/DragDropNode.cs b/AetherBags/Nodes/DragDropNode.cs index 7eaf992..b7c6bba 100644 --- a/AetherBags/Nodes/DragDropNode.cs +++ b/AetherBags/Nodes/DragDropNode.cs @@ -151,28 +151,6 @@ } } - public override ReadOnlySeString? Tooltip { - get; - set { - field = value; - switch (value) { - case { IsEmpty: false } when !TooltipRegistered: - AddEvent(AtkEventType.DragDropRollOver, ShowTooltip); - AddEvent(AtkEventType.DragDropRollOut, HideTooltip); - - TooltipRegistered = true; - break; - - case null when TooltipRegistered: - RemoveEvent(AtkEventType.DragDropRollOver, ShowTooltip); - RemoveEvent(AtkEventType.DragDropRollOut, HideTooltip); - - TooltipRegistered = false; - break; - } - } - } - private void DragDropInsertHandler(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData) { atkEvent->SetEventIsHandled(); diff --git a/AetherBags/Nodes/Input/TextInputWithHintNode.cs b/AetherBags/Nodes/Input/TextInputWithHintNode.cs index 5b7af51..8d84ff2 100644 --- a/AetherBags/Nodes/Input/TextInputWithHintNode.cs +++ b/AetherBags/Nodes/Input/TextInputWithHintNode.cs @@ -20,7 +20,7 @@ public class TextInputWithHintNode : SimpleComponentNode { TexturePath = "ui/uld/CircleButtons.tex", TextureCoordinates = new Vector2(112.0f, 84.0f), TextureSize = new Vector2(28.0f, 28.0f), - Tooltip = new SeStringBuilder() + TextTooltip = new SeStringBuilder() .Append("Supports Regex Search") .AppendNewLine() .Append("Start input with '$' to search by description") diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs index bd332bb..a30c507 100644 --- a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs @@ -5,6 +5,7 @@ using AetherBags.Helpers; using AetherBags.Inventory; using AetherBags.Nodes.Layout; using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; @@ -74,7 +75,7 @@ public class InventoryCategoryNode : SimpleComponentNode _categoryNameTextNode.String = _fullHeaderText; _categoryNameTextNode.TextColor = value.Category.Color; - _categoryNameTextNode.TooltipString = value.Category.Description; + _categoryNameTextNode.TextTooltip = value.Category.Description; UpdateItemGrid(); RecalculateSize(); @@ -209,7 +210,7 @@ public class InventoryCategoryNode : SimpleComponentNode CreateInventoryDragDropNode); } - private InventoryDragDropNode CreateInventoryDragDropNode(ItemInfo data) + private unsafe InventoryDragDropNode CreateInventoryDragDropNode(ItemInfo data) { InventoryItem item = data.Item; @@ -228,16 +229,18 @@ public class InventoryCategoryNode : SimpleComponentNode }, IsClickable = true, OnEnd = _ => System.AddonInventoryWindow.ManualInventoryRefresh(), - OnPayloadAccepted = (n, p) => OnPayloadAccepted(n, p, data), - OnRollOver = n => + OnPayloadAccepted = (node, payload) => OnPayloadAccepted(node, payload, data), + OnRollOver = node => { BeginHeaderHover(); - n.ShowInventoryItemTooltip(item.Container, item.Slot); + node.ShowInventoryItemTooltip(item.Container, item.Slot); }, - OnRollOut = n => + OnRollOut = node => { EndHeaderHover(); - n.HideTooltip(); + + ushort addonId = RaptureAtkUnitManager.Instance()->GetAddonByNode(node)->Id; + AtkStage.Instance()->TooltipManager.HideTooltip(addonId); }, ItemInfo = data }; diff --git a/KamiToolKit b/KamiToolKit index 6499c4c..9519b07 160000 --- a/KamiToolKit +++ b/KamiToolKit @@ -1 +1 @@ -Subproject commit 6499c4c309a912caf260f8d49e511738ed073209 +Subproject commit 9519b07c8db287ef75b7153a5e97c24574e800f2