From 47d5aa3bd67e15b655f1b3b01d86c828188c1eb6 Mon Sep 17 00:00:00 2001 From: Shawrkie Williams Date: Mon, 29 Dec 2025 15:36:06 -0500 Subject: [PATCH] WIP pinning and hoisting --- .../AddonCategoryConfigurationWindow.cs | 1 - AetherBags/Addons/AddonInventoryWindow.cs | 26 +- AetherBags/Addons/CategoryWrapper.cs | 2 - AetherBags/AetherBags.csproj | 2 +- AetherBags/Configuration/CategorySettings.cs | 1 + AetherBags/Configuration/CurrencySettings.cs | 1 - AetherBags/Configuration/GeneralSettings.cs | 3 - .../Configuration/SystemConfiguration.cs | 3 - .../Extensions/InventoryTypeExtensions.cs | 1 - AetherBags/Extensions/LoggerExtensions.cs | 2 - AetherBags/Helpers/ImportExportResetHelper.cs | 2 - AetherBags/Helpers/InventoryMoveHelper.cs | 4 - AetherBags/Hooks/InventoryHook.cs | 1 - AetherBags/Inventory/CategoryBucketManager.cs | 2 + AetherBags/Inventory/CategoryInfo.cs | 3 +- .../Inventory/InventoryNotificationState.cs | 1 - AetherBags/Inventory/InventoryState.cs | 40 +- AetherBags/Inventory/ItemInfo.cs | 2 - AetherBags/Inventory/LootedItemInfo.cs | 5 + AetherBags/Inventory/UserCategoryMatcher.cs | 1 - .../Nodes/Color/ColorPreviewButtonNode.cs | 2 - .../Category/CategoryConfigurationNode.cs | 2 - .../CategoryDefinitionConfigurationNode.cs | 16 + .../General/GeneralScrollingAreaNode.cs | 3 - .../Layout/LayoutConfigurationNode.cs | 7 +- AetherBags/Nodes/DragDropNode.cs | 2 - .../Nodes/Inventory/InventoryCategoryNode.cs | 4 +- .../InventoryCategoryPinCoordinator.cs | 45 ++ AetherBags/Nodes/Layout/WrappingGridNode.cs | 633 +++++++++++++++--- AetherBags/Plugin.cs | 20 +- AetherBags/Services.cs | 2 +- 31 files changed, 682 insertions(+), 157 deletions(-) create mode 100644 AetherBags/Inventory/LootedItemInfo.cs create mode 100644 AetherBags/Nodes/Inventory/InventoryCategoryPinCoordinator.cs diff --git a/AetherBags/Addons/AddonCategoryConfigurationWindow.cs b/AetherBags/Addons/AddonCategoryConfigurationWindow.cs index f40ea80..4d46049 100644 --- a/AetherBags/Addons/AddonCategoryConfigurationWindow.cs +++ b/AetherBags/Addons/AddonCategoryConfigurationWindow.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Numerics; diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index 93eeb92..05a93b4 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -1,19 +1,15 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; -using AetherBags.Extensions; using AetherBags.Inventory; -using AetherBags.Nodes; using AetherBags.Nodes.Input; 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; -using KamiToolKit.Classes; using KamiToolKit.Nodes; namespace AetherBags.Addons; @@ -21,6 +17,7 @@ namespace AetherBags.Addons; public class AddonInventoryWindow : NativeAddon { private readonly InventoryCategoryHoverCoordinator _hoverCoordinator = new(); + private readonly InventoryCategoryPinCoordinator _pinCoordinator = new(); private readonly HashSet _hoverSubscribed = new(); private InventoryNotificationNode _notificationNode = null!; @@ -135,6 +132,20 @@ public class AddonInventoryWindow : NativeAddon RefreshCategoriesCore(true); } + public void UpdateLootedCategory(IReadOnlyList lootedItemInfos) + { + if (!Services.ClientState.IsLoggedIn) return; + _recentlyLootedCategoryNode?.CategorizedInventory.Items.AddRange( + lootedItemInfos.Select(x => new ItemInfo + { + ItemCount = x.Quantity, + Key = uint.MaxValue - 1, + Item = x.Item, + }) + .ToList()); + RefreshCategoriesCore(true); + } + public void ManualCurrencyRefresh() { if (!Services.ClientState.IsLoggedIn) return; @@ -182,6 +193,10 @@ public class AddonInventoryWindow : NativeAddon Size = ContentSize with { Y = 120 }, }); + bool pinsChanged = _pinCoordinator.ApplyPinnedStates(_categoriesNode); + if (pinsChanged) + _hoverCoordinator.ResetAll(_categoriesNode); + WireHoverHandlers(); if (autosize) AutoSizeWindow(); @@ -192,6 +207,7 @@ public class AddonInventoryWindow : NativeAddon } } + private void WireHoverHandlers() { var nodes = _categoriesNode.Nodes; diff --git a/AetherBags/Addons/CategoryWrapper.cs b/AetherBags/Addons/CategoryWrapper.cs index 9ccf226..790571c 100644 --- a/AetherBags/Addons/CategoryWrapper.cs +++ b/AetherBags/Addons/CategoryWrapper.cs @@ -1,8 +1,6 @@ using AetherBags.Configuration; using AetherBags.Inventory; -using Dalamud.Game.Text.SeStringHandling; using KamiToolKit.Premade; -using SeStringBuilder = Lumina.Text.SeStringBuilder; namespace AetherBags.Addons; diff --git a/AetherBags/AetherBags.csproj b/AetherBags/AetherBags.csproj index 0773422..0422e06 100644 --- a/AetherBags/AetherBags.csproj +++ b/AetherBags/AetherBags.csproj @@ -4,7 +4,7 @@ - Zeffuro + Zeffuro, Pie Lover AetherBags AetherBags Never think too hard about your bags again! diff --git a/AetherBags/Configuration/CategorySettings.cs b/AetherBags/Configuration/CategorySettings.cs index f831eac..898a201 100644 --- a/AetherBags/Configuration/CategorySettings.cs +++ b/AetherBags/Configuration/CategorySettings.cs @@ -17,6 +17,7 @@ public class CategorySettings public class UserCategoryDefinition { public bool Enabled { get; set; } = true; + public bool Pinned { get; set; } = false; public string Id { get; set; } = Guid.NewGuid().ToString("N"); public string Name { get; set; } = "New Category"; public string Description { get; set; } = string.Empty; diff --git a/AetherBags/Configuration/CurrencySettings.cs b/AetherBags/Configuration/CurrencySettings.cs index 0b5ad3f..505ef00 100644 --- a/AetherBags/Configuration/CurrencySettings.cs +++ b/AetherBags/Configuration/CurrencySettings.cs @@ -1,6 +1,5 @@ using System.Numerics; using KamiToolKit.Classes; -using SixLabors.ImageSharp.PixelFormats; namespace AetherBags.Configuration; diff --git a/AetherBags/Configuration/GeneralSettings.cs b/AetherBags/Configuration/GeneralSettings.cs index 72296a8..c8e8b3c 100644 --- a/AetherBags/Configuration/GeneralSettings.cs +++ b/AetherBags/Configuration/GeneralSettings.cs @@ -1,6 +1,3 @@ -using System.Numerics; -using KamiToolKit.Classes; - namespace AetherBags.Configuration; public class GeneralSettings diff --git a/AetherBags/Configuration/SystemConfiguration.cs b/AetherBags/Configuration/SystemConfiguration.cs index 4e58599..78b0ead 100644 --- a/AetherBags/Configuration/SystemConfiguration.cs +++ b/AetherBags/Configuration/SystemConfiguration.cs @@ -1,6 +1,3 @@ -using System.Numerics; -using KamiToolKit.Classes; - namespace AetherBags.Configuration; public class SystemConfiguration diff --git a/AetherBags/Extensions/InventoryTypeExtensions.cs b/AetherBags/Extensions/InventoryTypeExtensions.cs index df91f0b..37a2b7d 100644 --- a/AetherBags/Extensions/InventoryTypeExtensions.cs +++ b/AetherBags/Extensions/InventoryTypeExtensions.cs @@ -1,4 +1,3 @@ -using System; using AetherBags.Inventory; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI.Misc; diff --git a/AetherBags/Extensions/LoggerExtensions.cs b/AetherBags/Extensions/LoggerExtensions.cs index c0a9765..dd6e759 100644 --- a/AetherBags/Extensions/LoggerExtensions.cs +++ b/AetherBags/Extensions/LoggerExtensions.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; - namespace AetherBags.Extensions; public static class LoggerExtensions diff --git a/AetherBags/Helpers/ImportExportResetHelper.cs b/AetherBags/Helpers/ImportExportResetHelper.cs index 1b7a2a2..49a4d5d 100644 --- a/AetherBags/Helpers/ImportExportResetHelper.cs +++ b/AetherBags/Helpers/ImportExportResetHelper.cs @@ -1,8 +1,6 @@ -using System.Linq; using AetherBags.Configuration; using AetherBags.Helpers.Import; using Dalamud.Bindings.ImGui; -using Dalamud.Game.ClientState.Keys; using Dalamud.Interface.ImGuiNotification; namespace AetherBags.Helpers; diff --git a/AetherBags/Helpers/InventoryMoveHelper.cs b/AetherBags/Helpers/InventoryMoveHelper.cs index 1763765..9fef5cc 100644 --- a/AetherBags/Helpers/InventoryMoveHelper.cs +++ b/AetherBags/Helpers/InventoryMoveHelper.cs @@ -1,8 +1,4 @@ -using AetherBags. Extensions; using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Component. GUI; -using ValueType = FFXIVClientStructs. FFXIV. Component.GUI.ValueType; namespace AetherBags. Helpers; diff --git a/AetherBags/Hooks/InventoryHook.cs b/AetherBags/Hooks/InventoryHook.cs index 1d163ac..f5df87c 100644 --- a/AetherBags/Hooks/InventoryHook.cs +++ b/AetherBags/Hooks/InventoryHook.cs @@ -1,7 +1,6 @@ using System; using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; diff --git a/AetherBags/Inventory/CategoryBucketManager.cs b/AetherBags/Inventory/CategoryBucketManager.cs index baf8a7f..a565ee3 100644 --- a/AetherBags/Inventory/CategoryBucketManager.cs +++ b/AetherBags/Inventory/CategoryBucketManager.cs @@ -73,6 +73,7 @@ public static class CategoryBucketManager Name = category.Name, Description = category.Description, Color = category.Color, + IsPinned = category.Pinned, }, Items = new List(capacity: 16), FilteredItems = new List(capacity: 16), @@ -86,6 +87,7 @@ public static class CategoryBucketManager bucket.Category.Name = category.Name; bucket.Category.Description = category.Description; bucket.Category.Color = category.Color; + bucket.Category.IsPinned = category.Pinned; } foreach (var itemKvp in itemInfoByKey) diff --git a/AetherBags/Inventory/CategoryInfo.cs b/AetherBags/Inventory/CategoryInfo.cs index 542e5ea..819d75e 100644 --- a/AetherBags/Inventory/CategoryInfo.cs +++ b/AetherBags/Inventory/CategoryInfo.cs @@ -6,6 +6,7 @@ namespace AetherBags.Inventory; public class CategoryInfo { public required string Name { get; set; } - public Vector4 Color { get; set; } = ColorHelper.GetColor(50); + public Vector4 Color { get; set; } = ColorHelper.GetColor(2); public string Description { get; set; } = string.Empty; + public bool IsPinned { get; set; } = false; } \ No newline at end of file diff --git a/AetherBags/Inventory/InventoryNotificationState.cs b/AetherBags/Inventory/InventoryNotificationState.cs index fd82fc5..6ce636b 100644 --- a/AetherBags/Inventory/InventoryNotificationState.cs +++ b/AetherBags/Inventory/InventoryNotificationState.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Lumina.Excel; using Lumina.Excel.Sheets; using Lumina.Text.ReadOnly; diff --git a/AetherBags/Inventory/InventoryState.cs b/AetherBags/Inventory/InventoryState.cs index 0d354ca..d185956 100644 --- a/AetherBags/Inventory/InventoryState.cs +++ b/AetherBags/Inventory/InventoryState.cs @@ -1,10 +1,10 @@ using AetherBags.Configuration; +using AetherBags.Currency; using Dalamud.Game.Inventory; +using Dalamud.Game.Inventory.InventoryEventArgTypes; using FFXIVClientStructs.FFXIV.Client.Game; using System.Collections.Generic; using System.Linq; -using AetherBags.Currency; -using CurrencyManager = FFXIVClientStructs.FFXIV.Client.Game.CurrencyManager; namespace AetherBags.Inventory; @@ -21,6 +21,9 @@ public static unsafe class InventoryState private static readonly List UserCategoriesSortedScratch = new(capacity: 64); private static readonly List RemoveKeysScratch = new(capacity: 256); private static readonly HashSet ClaimedKeys = new(capacity: 512); + private static readonly List? LootedItems = new(capacity: 512); + + public static bool TrackLootedItems = false; public static bool Contains(this IReadOnlyCollection inventoryTypes, GameInventoryType type) => inventoryTypes.Contains((InventoryType)type); @@ -135,6 +138,38 @@ public static unsafe class InventoryState public static InventoryContainer* GetInventoryContainer(InventoryType inventoryType) => InventoryScanner.GetInventoryContainer(inventoryType); + internal static void OnRawItemAdded(IReadOnlyCollection events) + { + if (!TrackLootedItems) return; + + bool updateRequested = false; + + foreach (var eventData in events) + { + if (!StandardInventories.Contains(eventData.Item.ContainerType)) continue; + + if (!Services.ClientState.IsLoggedIn) return; + if (eventData is not (InventoryItemAddedArgs or InventoryItemChangedArgs)) return; + if (eventData is InventoryItemChangedArgs changedArgs && changedArgs.OldItemState.Quantity >= changedArgs.Item.Quantity) return; + + var inventoryItem = (InventoryItem*)eventData.Item.Address; + var changeAmount = eventData is InventoryItemChangedArgs changed ? changed.Item.Quantity - changed.OldItemState.Quantity : eventData.Item.Quantity; + + LootedItems?.Add(new LootedItemInfo( + LootedItems.Count, + *inventoryItem, + changeAmount) + ); + + updateRequested = true; + } + + if (updateRequested) + { + System.AddonInventoryWindow?.UpdateLootedCategory(LootedItems ?? []); + } + } + private static void ClearAll() { AggByKey.Clear(); @@ -152,5 +187,6 @@ public static unsafe class InventoryState FilteredCategories.Clear(); RemoveKeysScratch.Clear(); ClaimedKeys.Clear(); + LootedItems?.Clear(); } } \ No newline at end of file diff --git a/AetherBags/Inventory/ItemInfo.cs b/AetherBags/Inventory/ItemInfo.cs index 3083413..15e3a7d 100644 --- a/AetherBags/Inventory/ItemInfo.cs +++ b/AetherBags/Inventory/ItemInfo.cs @@ -1,11 +1,9 @@ -using AetherBags.Extensions; using FFXIVClientStructs.FFXIV.Client.Game; using Lumina.Excel; using Lumina.Excel.Sheets; using System; using System.Numerics; using System.Text.RegularExpressions; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; namespace AetherBags.Inventory; diff --git a/AetherBags/Inventory/LootedItemInfo.cs b/AetherBags/Inventory/LootedItemInfo.cs new file mode 100644 index 0000000..3d4edd9 --- /dev/null +++ b/AetherBags/Inventory/LootedItemInfo.cs @@ -0,0 +1,5 @@ +using FFXIVClientStructs.FFXIV.Client.Game; + +namespace AetherBags.Inventory; + +public record LootedItemInfo(int Index, InventoryItem Item, int Quantity); \ No newline at end of file diff --git a/AetherBags/Inventory/UserCategoryMatcher.cs b/AetherBags/Inventory/UserCategoryMatcher.cs index 4e2e29b..945b291 100644 --- a/AetherBags/Inventory/UserCategoryMatcher.cs +++ b/AetherBags/Inventory/UserCategoryMatcher.cs @@ -1,6 +1,5 @@ using AetherBags.Configuration; using System; -using System.Collections.Generic; using System.Text.RegularExpressions; namespace AetherBags.Inventory; diff --git a/AetherBags/Nodes/Color/ColorPreviewButtonNode.cs b/AetherBags/Nodes/Color/ColorPreviewButtonNode.cs index 3c48edb..28cf618 100644 --- a/AetherBags/Nodes/Color/ColorPreviewButtonNode.cs +++ b/AetherBags/Nodes/Color/ColorPreviewButtonNode.cs @@ -1,7 +1,5 @@ using System.Numerics; -using Dalamud.Game.Addon.Events.EventDataTypes; using KamiToolKit.Nodes; -using ColorPreviewNode = AetherBags.Nodes.Color.ColorPreviewNode; namespace AetherBags.Nodes.Color; diff --git a/AetherBags/Nodes/Configuration/Category/CategoryConfigurationNode.cs b/AetherBags/Nodes/Configuration/Category/CategoryConfigurationNode.cs index 8a8eb5a..f2ddd6c 100644 --- a/AetherBags/Nodes/Configuration/Category/CategoryConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/Category/CategoryConfigurationNode.cs @@ -1,7 +1,5 @@ using System; -using System.Numerics; using AetherBags.Addons; -using AetherBags.Configuration; using KamiToolKit.Nodes; using KamiToolKit.Premade.Nodes; diff --git a/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs b/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs index 29666ec..3b76482 100644 --- a/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs @@ -17,6 +17,7 @@ namespace AetherBags.Nodes.Configuration.Category; public sealed class CategoryDefinitionConfigurationNode : VerticalListNode { private readonly CheckboxNode _enabledCheckbox; + private readonly CheckboxNode _pinnedCheckbox; private readonly TextInputNode _nameInputNode; private readonly TextInputNode _descriptionInputNode; private readonly ColorInputRow _colorInputNode; @@ -98,6 +99,20 @@ public sealed class CategoryDefinitionConfigurationNode : VerticalListNode }; AddNode(_enabledCheckbox); + _pinnedCheckbox = new CheckboxNode + { + Size = new Vector2(200, 20), + String = "Pinned", + IsChecked = CategoryDefinition.Pinned, + OnClick = isChecked => + { + CategoryDefinition.Pinned = isChecked; + NotifyChanged(); + NotifyCategoryPropertyChanged(); + }, + }; + AddNode(_pinnedCheckbox); + AddNode(new LabelTextNode { TextFlags = TextFlags.AutoAdjustNodeSize, @@ -471,6 +486,7 @@ public sealed class CategoryDefinitionConfigurationNode : VerticalListNode if (! _isInitialized) return; _enabledCheckbox.IsChecked = CategoryDefinition.Enabled; + _pinnedCheckbox.IsChecked = CategoryDefinition.Pinned; _colorInputNode.CurrentColor = CategoryDefinition.Color; _nameInputNode.String = CategoryDefinition.Name; _descriptionInputNode.String = CategoryDefinition.Description; diff --git a/AetherBags/Nodes/Configuration/General/GeneralScrollingAreaNode.cs b/AetherBags/Nodes/Configuration/General/GeneralScrollingAreaNode.cs index 1abe0e9..253f864 100644 --- a/AetherBags/Nodes/Configuration/General/GeneralScrollingAreaNode.cs +++ b/AetherBags/Nodes/Configuration/General/GeneralScrollingAreaNode.cs @@ -1,9 +1,6 @@ -using System; -using System.Linq; using System.Numerics; using AetherBags.Configuration; using AetherBags.Nodes.Configuration.Layout; -using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Nodes; namespace AetherBags.Nodes.Configuration.General; diff --git a/AetherBags/Nodes/Configuration/Layout/LayoutConfigurationNode.cs b/AetherBags/Nodes/Configuration/Layout/LayoutConfigurationNode.cs index 0669999..93db895 100644 --- a/AetherBags/Nodes/Configuration/Layout/LayoutConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/Layout/LayoutConfigurationNode.cs @@ -1,7 +1,6 @@ using System.Numerics; using AetherBags.Configuration; using KamiToolKit.Nodes; -using KamiToolKit.Classes; namespace AetherBags.Nodes.Configuration.Layout; @@ -40,7 +39,7 @@ internal class LayoutConfigurationNode : TabbedVerticalListNode var compactPackingCheckboxNode = new CheckboxNode { - Size = Size with { Y = 18 }, + Height = 18, IsVisible = true, String = "Use Compact Packing", IsChecked = config.CompactPackingEnabled, @@ -58,7 +57,7 @@ internal class LayoutConfigurationNode : TabbedVerticalListNode AddTab(1); _preferLargestFitCheckboxNode = new CheckboxNode { - Size = Size with { Y = 18 }, + Height = 18, IsVisible = true, String = "Prefer Largest Fit", IsEnabled = config.CompactPackingEnabled, @@ -73,7 +72,7 @@ internal class LayoutConfigurationNode : TabbedVerticalListNode _useStableInsertCheckboxNode = new CheckboxNode { - Size = Size with { Y = 18 }, + Height = 18, IsVisible = true, String = "Use Stable Insert", IsEnabled = config.CompactPackingEnabled, diff --git a/AetherBags/Nodes/DragDropNode.cs b/AetherBags/Nodes/DragDropNode.cs index 26bc018..f99f3b2 100644 --- a/AetherBags/Nodes/DragDropNode.cs +++ b/AetherBags/Nodes/DragDropNode.cs @@ -1,6 +1,5 @@ using System; using System.Numerics; -using AetherBags.Extensions; using AetherBags.Interop; using FFXIVClientStructs.FFXIV.Client.Enums; using FFXIVClientStructs.FFXIV.Client.UI; @@ -9,7 +8,6 @@ using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Classes.Timelines; using KamiToolKit.Nodes; -using Lumina.Text.ReadOnly; namespace AetherBags.Nodes; diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs index 12b6180..da78a77 100644 --- a/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryCategoryNode.cs @@ -1,12 +1,10 @@ using System; using System.Numerics; -using AetherBags.Extensions; using AetherBags.Helpers; using AetherBags.Inventory; using AetherBags.Nodes.Layout; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Misc; using FFXIVClientStructs.FFXIV.Component.GUI; using KamiToolKit.Classes; using KamiToolKit.Nodes; @@ -107,6 +105,8 @@ public class InventoryCategoryNode : SimpleComponentNode } } + public bool IsPinnedInConfig => CategorizedInventory.Category?.IsPinned ?? false; + public void BeginHeaderHover() { _hoverRefs++; diff --git a/AetherBags/Nodes/Inventory/InventoryCategoryPinCoordinator.cs b/AetherBags/Nodes/Inventory/InventoryCategoryPinCoordinator.cs new file mode 100644 index 0000000..8683104 --- /dev/null +++ b/AetherBags/Nodes/Inventory/InventoryCategoryPinCoordinator.cs @@ -0,0 +1,45 @@ +using AetherBags.Nodes.Layout; + +namespace AetherBags.Nodes.Inventory; + +public sealed class InventoryCategoryPinCoordinator +{ + public bool ApplyPinnedStates(WrappingGridNode grid) + { + bool changed = false; + + using (grid.DeferRecalculateLayout()) + { + foreach (var node in grid.GetNodes()) + { + bool shouldBePinned = node.IsPinnedInConfig; + + bool isPinned = grid.IsPinned(node); + + if (shouldBePinned) + { + if (!isPinned) + { + grid.PinNode(node); + changed = true; + } + } + else + { + if (isPinned) + { + grid.UnpinNode(node); + changed = true; + } + } + } + } + + return changed; + } + + public bool PrunePinnedNotInGrid(WrappingGridNode grid) + { + return false; + } +} \ No newline at end of file diff --git a/AetherBags/Nodes/Layout/WrappingGridNode.cs b/AetherBags/Nodes/Layout/WrappingGridNode.cs index 385c503..2a5d1d4 100644 --- a/AetherBags/Nodes/Layout/WrappingGridNode.cs +++ b/AetherBags/Nodes/Layout/WrappingGridNode.cs @@ -1,9 +1,9 @@ +using KamiToolKit; +using KamiToolKit.Nodes; using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -using KamiToolKit; -using KamiToolKit.Nodes; namespace AetherBags.Nodes.Layout; @@ -36,8 +36,18 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase private bool _lastuseStableInsert; private int _lastCompactLookahead; + private int _deferRecalcDepth; + private bool _pendingRecalc; + private int[] _orderScratch = Array.Empty(); + private T? _hoistedNode; + private readonly HashSet _pinned = new(ReferenceEqualityComparer.Instance); + + private readonly List _layoutOrder = new(capacity: 256); + private readonly List _pinnedScratch = new(capacity: 64); + private readonly List _normalScratch = new(capacity: 256); + public WrappingGridNode() { _rowsView = new RowsReadOnlyView(_rows); @@ -45,13 +55,61 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase public IReadOnlyList> Rows => _rowsView; + public T? HoistedNode => _hoistedNode; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetRowIndex(NodeBase node, out int rowIndex) => _rowIndex.TryGetValue(node, out rowIndex); + public void SetHoistedNode(T? node) + { + if (ReferenceEquals(_hoistedNode, node)) + return; + + _hoistedNode = node; + + if (node is not null) + { + if (!NodeList.Contains(node)) + AddNode(node); + } + + RecalculateLayout(); + } + + public bool PinNode(T node) + { + if (_pinned.Add(node)) + { + RequestRecalculateLayout(); + return true; + } + return false; + } + + public bool UnpinNode(T node) + { + if (_pinned.Remove(node)) + { + RequestRecalculateLayout(); + return true; + } + return false; + } + + public void ClearPinned() + { + if (_pinned.Count == 0) return; + _pinned.Clear(); + RequestRecalculateLayout(); + } + + public bool IsPinned(T node) => _pinned.Contains(node); + protected override void InternalRecalculateLayout() { - int count = NodeList.Count; - if (count == 0) + int layoutCount = BuildLayoutOrder(out int hoistedCount, out int pinnedCount); + + if (layoutCount == 0) { RecycleAllRows(); _rowIndex.Clear(); @@ -61,9 +119,20 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase return; } - if (System.Config.General.CompactPackingEnabled) + bool hasSpecials = hoistedCount != 0 || pinnedCount != 0; + bool compactEnabled = System.Config.General.CompactPackingEnabled; + + if (compactEnabled) { - if (_rows.Count != 0 && LayoutParamsMatchLast() && NodeSetMatchesExistingLayout(count)) + if (hasSpecials) + { + FullReflowCompactSections(layoutCount, hoistedCount, pinnedCount); + _requiredHeightDirty = true; + RememberLayoutParams(); + return; + } + + if (_rows.Count != 0 && LayoutParamsMatchLast() && NodeSetMatchesExistingLayout(layoutCount)) { RepositionExistingRows(); _requiredHeightDirty = true; @@ -71,44 +140,108 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase return; } - FullReflowCompact(count); + FullReflowCompact(layoutCount); _requiredHeightDirty = true; RememberLayoutParams(); return; } - if (_rows.Count != 0 && TryUpdateLayoutWithoutReflowOrTailReflow(count)) + if (_rows.Count != 0 && + NodeSetMatchesExistingLayout(layoutCount) && + TryUpdateLayoutWithoutReflowOrTailReflow(layoutCount, hoistedCount, pinnedCount)) { _requiredHeightDirty = true; RememberLayoutParams(); return; } - FullReflow(count); + FullReflowOrdered(layoutCount, hoistedCount, pinnedCount); _requiredHeightDirty = true; RememberLayoutParams(); } - private bool NodeSetMatchesExistingLayout(int count) + private int BuildLayoutOrder(out int hoistedCount, out int pinnedCount) { - if (_rowIndex.Count != count) + _layoutOrder.Clear(); + _pinnedScratch.Clear(); + _normalScratch.Clear(); + + int nodeCount = NodeList.Count; + if (nodeCount == 0) + { + _hoistedNode = null; + if (_pinned.Count != 0) _pinned.Clear(); + + hoistedCount = 0; + pinnedCount = 0; + return 0; + } + + var present = new HashSet(ReferenceEqualityComparer.Instance); + + bool hoistedPresent = false; + T? hoisted = _hoistedNode; + + for (int i = 0; i < nodeCount; i++) + { + if (NodeList[i] is not T node) + continue; + + present.Add(node); + + if (hoisted != null && ReferenceEquals(node, hoisted)) + { + hoistedPresent = true; + continue; + } + + if (_pinned.Contains(node)) + _pinnedScratch.Add(node); + else + _normalScratch.Add(node); + } + + if (_pinned.Count != 0) + _pinned.RemoveWhere(n => !present.Contains(n)); + + if (hoisted != null && !hoistedPresent) + _hoistedNode = null; + + if (hoistedPresent && hoisted != null) + _layoutOrder.Add(hoisted); + + for (int i = 0; i < _pinnedScratch.Count; i++) + _layoutOrder.Add(_pinnedScratch[i]); + + for (int i = 0; i < _normalScratch.Count; i++) + _layoutOrder.Add(_normalScratch[i]); + + hoistedCount = (hoistedPresent && hoisted != null) ? 1 : 0; + pinnedCount = _pinnedScratch.Count; + return _layoutOrder.Count; + } + + + private bool NodeSetMatchesExistingLayout(int layoutCount) + { + if (_rowIndex.Count != layoutCount) return false; - for (int i = 0; i < count; i++) + for (int i = 0; i < layoutCount; i++) { - if (!_rowIndex.ContainsKey(NodeList[i])) + if (!_rowIndex.ContainsKey(_layoutOrder[i])) return false; } return true; } - private bool TryUpdateLayoutWithoutReflowOrTailReflow(int count) + private bool TryUpdateLayoutWithoutReflowOrTailReflow(int layoutCount, int hoistedCount, int pinnedCount) { if (!LayoutParamsMatchLast()) return false; - int mismatchRow = FindFirstMismatchRow(count, out int mismatchNodeIndex); + int mismatchRow = FindFirstMismatchRow(layoutCount, hoistedCount, pinnedCount, out int mismatchNodeIndex); if (mismatchRow < 0) { @@ -116,20 +249,22 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase return true; } - TailReflowFrom(mismatchRow, mismatchNodeIndex, count); + TailReflowFrom(mismatchRow, mismatchNodeIndex, layoutCount, hoistedCount, pinnedCount); return true; } - private int FindFirstMismatchRow(int count, out int mismatchNodeIndex) + private int FindFirstMismatchRow(int layoutCount, int hoistedCount, int pinnedCount, out int mismatchNodeIndex) { float availableWidth = Width; float hSpace = HorizontalSpacing; float startX = FirstItemSpacing; + int normalStart = hoistedCount + pinnedCount; + int rowIdx = 0; int nodeIdx = 0; - while (nodeIdx < count) + while (nodeIdx < layoutCount) { if (rowIdx >= _rows.Count) { @@ -146,19 +281,33 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase return rowIdx; } - int predictedCount = 0; - float currentX = startX; + int predictedCount; - while (nodeIdx + predictedCount < count) + if (hoistedCount != 0 && nodeIdx == 0) { - NodeBase node = NodeList[nodeIdx + predictedCount]; - float w = node.Width; + predictedCount = 1; + } + else + { + int sectionEnd = nodeIdx < normalStart ? normalStart : layoutCount; - if (predictedCount != 0 && (currentX + w) > availableWidth) - break; + predictedCount = 0; + float currentX = startX; - predictedCount++; - currentX += w + hSpace; + while (nodeIdx + predictedCount < sectionEnd) + { + NodeBase node = _layoutOrder[nodeIdx + predictedCount]; + float w = node.Width; + + if (predictedCount != 0 && (currentX + w) > availableWidth) + break; + + predictedCount++; + currentX += w + hSpace; + } + + if (predictedCount == 0 && nodeIdx < sectionEnd) + predictedCount = 1; } if (predictedCount != existingRowCount) @@ -169,7 +318,7 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase for (int j = 0; j < existingRowCount; j++) { - if (!ReferenceEquals(existingRow[j], NodeList[nodeIdx + j])) + if (!ReferenceEquals(existingRow[j], _layoutOrder[nodeIdx + j])) { mismatchNodeIndex = nodeIdx; return rowIdx; @@ -193,7 +342,7 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase private void RepositionExistingRows() { _rowIndex.Clear(); - _rowIndex.EnsureCapacity(NodeList.Count); + _rowIndex.EnsureCapacity(_layoutOrder.Count); float hSpace = HorizontalSpacing; float vSpace = VerticalSpacing; @@ -228,10 +377,10 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase } } - private void TailReflowFrom(int startRowIndex, int startNodeIndex, int count) + private void TailReflowFrom(int startRowIndex, int startNodeIndex, int layoutCount, int hoistedCount, int pinnedCount) { _rowIndex.Clear(); - _rowIndex.EnsureCapacity(count); + _rowIndex.EnsureCapacity(layoutCount); float availableWidth = Width; float hSpace = HorizontalSpacing; @@ -277,109 +426,359 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase _rows.RemoveAt(i); } - int currentRowIndex = startRowIndex; - float xCursor = startX; - float rowHeightTail = 0f; + int normalStart = hoistedCount + pinnedCount; - List currentRow = RentRowList(capacityHint: 8); + int rowIndex = startRowIndex; + int idx = startNodeIndex; - for (int i = startNodeIndex; i < count; i++) + while (idx < layoutCount) { - NodeBase node = NodeList[i]; - float w = node.Width; + List row = RentRowList(capacityHint: 8); - if (currentRow.Count != 0 && (xCursor + w) > availableWidth) + float x = startX; + float rowHeight = 0f; + + if (hoistedCount != 0 && idx == 0) { - _rows.Add(currentRow); - currentRowIndex++; + NodeBase node = _layoutOrder[0]; - y += rowHeightTail + vSpace; - xCursor = startX; - rowHeightTail = 0f; + node.X = x; + node.Y = y; - currentRow = RentRowList(capacityHint: 8); + AdjustNode(node); + + rowHeight = node.Height; + row.Add(node); + _rowIndex[node] = rowIndex; + + idx = 1; + } + else + { + int sectionEnd = idx < normalStart ? normalStart : layoutCount; + + while (idx < sectionEnd) + { + NodeBase node = _layoutOrder[idx]; + float w = node.Width; + + if (row.Count != 0 && (x + w) > availableWidth) + break; + + node.X = x; + node.Y = y; + + AdjustNode(node); + + float h = node.Height; + if (h > rowHeight) rowHeight = h; + + row.Add(node); + _rowIndex[node] = rowIndex; + + x += w + hSpace; + idx++; + } + + if (row.Count == 0 && idx < sectionEnd) + { + NodeBase node = _layoutOrder[idx]; + + node.X = startX; + node.Y = y; + + AdjustNode(node); + + rowHeight = node.Height; + + row.Add(node); + _rowIndex[node] = rowIndex; + + idx++; + } } - node.X = xCursor; - node.Y = y; - - AdjustNode(node); - - float h = node.Height; - if (h > rowHeightTail) rowHeightTail = h; - - currentRow.Add(node); - _rowIndex[node] = currentRowIndex; - - xCursor += w + hSpace; - } - - if (currentRow.Count != 0) - { - _rows.Add(currentRow); - } - else - { - RecycleRow(currentRow); + if (row.Count != 0) + { + _rows.Add(row); + rowIndex++; + y += rowHeight + vSpace; + } + else + { + RecycleRow(row); + break; + } } } - private void FullReflow(int count) + private void FullReflowOrdered(int layoutCount, int hoistedCount, int pinnedCount) { RecycleAllRows(); _rowIndex.Clear(); - _rowIndex.EnsureCapacity(count); + _rowIndex.EnsureCapacity(layoutCount); float availableWidth = Width; float hSpace = HorizontalSpacing; float vSpace = VerticalSpacing; float startX = FirstItemSpacing; - float currentX = startX; - float currentY = TopPadding; - float rowHeight = 0f; + float y = TopPadding; - int currentRowIndex = 0; - List currentRow = RentRowList(capacityHint: 8); + int normalStart = hoistedCount + pinnedCount; - for (int i = 0; i < count; i++) + int rowIdx = 0; + int idx = 0; + + while (idx < layoutCount) { - NodeBase node = NodeList[i]; - float nodeWidth = node.Width; + List row = RentRowList(capacityHint: 8); - if (currentRow.Count != 0 && (currentX + nodeWidth) > availableWidth) + float x = startX; + float rowHeight = 0f; + + if (hoistedCount != 0 && idx == 0) { - _rows.Add(currentRow); - currentRowIndex++; + NodeBase node = _layoutOrder[0]; - currentY += rowHeight + vSpace; - currentX = startX; - rowHeight = 0f; + node.X = x; + node.Y = y; - currentRow = RentRowList(capacityHint: 8); + AdjustNode(node); + + rowHeight = node.Height; + + row.Add(node); + _rowIndex[node] = rowIdx; + + idx = 1; + } + else + { + int sectionEnd = idx < normalStart ? normalStart : layoutCount; + + while (idx < sectionEnd) + { + NodeBase node = _layoutOrder[idx]; + float w = node.Width; + + if (row.Count != 0 && (x + w) > availableWidth) + break; + + node.X = x; + node.Y = y; + + AdjustNode(node); + + float h = node.Height; + if (h > rowHeight) rowHeight = h; + + row.Add(node); + _rowIndex[node] = rowIdx; + + x += w + hSpace; + idx++; + } + + if (row.Count == 0 && idx < sectionEnd) + { + NodeBase node = _layoutOrder[idx]; + + node.X = startX; + node.Y = y; + + AdjustNode(node); + + rowHeight = node.Height; + + row.Add(node); + _rowIndex[node] = rowIdx; + + idx++; + } } - node.X = currentX; - node.Y = currentY; + if (row.Count != 0) + { + _rows.Add(row); + rowIdx++; + y += rowHeight + vSpace; + } + else + { + RecycleRow(row); + break; + } + } + } + + private void FullReflowCompactSections(int layoutCount, int hoistedCount, int pinnedCount) + { + RecycleAllRows(); + _rowIndex.Clear(); + _rowIndex.EnsureCapacity(layoutCount); + + float vSpace = VerticalSpacing; + float y = TopPadding; + + int rowIdx = 0; + int idx = 0; + + if (hoistedCount != 0) + { + NodeBase node = _layoutOrder[0]; + List row = RentRowList(capacityHint: 1); + + node.X = FirstItemSpacing; + node.Y = y; AdjustNode(node); - float nodeHeight = node.Height; - if (nodeHeight > rowHeight) rowHeight = nodeHeight; + row.Add(node); + _rowIndex[node] = rowIdx; - currentRow.Add(node); - _rowIndex[node] = currentRowIndex; + _rows.Add(row); - currentX += nodeWidth + hSpace; + y += node.Height + vSpace; + rowIdx++; + idx = 1; } - if (currentRow.Count != 0) + int pinnedStart = idx; + int pinnedEnd = pinnedStart + pinnedCount; + if (pinnedCount > 0) { - _rows.Add(currentRow); + PackSectionCompact(pinnedStart, pinnedEnd, ref y, ref rowIdx); + idx = pinnedEnd; } - else + + if (idx < layoutCount) { - RecycleRow(currentRow); + PackSectionCompact(idx, layoutCount, ref y, ref rowIdx); + } + } + + private void PackSectionCompact(int startIndex, int endIndex, ref float y, ref int rowIdx) + { + int sectionCount = endIndex - startIndex; + if (sectionCount <= 0) + return; + + float availableWidth = Width; + float hSpace = HorizontalSpacing; + float vSpace = VerticalSpacing; + float startX = FirstItemSpacing; + + EnsureOrderScratch(sectionCount); + for (int i = 0; i < sectionCount; i++) + _orderScratch[i] = i; + + int lookahead = System.Config.General.CompactLookahead; + if (lookahead < 0) lookahead = 0; + + int p = 0; + + while (p < sectionCount) + { + List row = RentRowList(capacityHint: 8); + + float x = startX; + float rowHeight = 0f; + + while (p < sectionCount) + { + int localIdx = _orderScratch[p]; + NodeBase node = _layoutOrder[startIndex + localIdx]; + float w = node.Width; + + if (row.Count == 0 || (x + w) <= availableWidth) + { + node.X = x; + node.Y = y; + + AdjustNode(node); + + float h = node.Height; + if (h > rowHeight) rowHeight = h; + + row.Add(node); + _rowIndex[node] = rowIdx; + + x += w + hSpace; + p++; + continue; + } + + int bestPos = -1; + float bestWidth = 0f; + + int end = p + lookahead; + if (end >= sectionCount) end = sectionCount - 1; + + for (int s = p + 1; s <= end; s++) + { + int candLocalIdx = _orderScratch[s]; + NodeBase cand = _layoutOrder[startIndex + candLocalIdx]; + float cw = cand.Width; + + if ((x + cw) <= availableWidth) + { + if (!System.Config.General.CompactPreferLargestFit) + { + bestPos = s; + break; + } + + if (cw > bestWidth) + { + bestWidth = cw; + bestPos = s; + } + } + } + + if (bestPos < 0) + break; + + if (bestPos != p) + { + int chosen = _orderScratch[bestPos]; + + if (System.Config.General.CompactStableInsert) + { + Array.Copy(_orderScratch, p, _orderScratch, p + 1, bestPos - p); + _orderScratch[p] = chosen; + } + else + { + _orderScratch[bestPos] = _orderScratch[p]; + _orderScratch[p] = chosen; + } + } + } + + if (row.Count == 0) + { + int localIdx = _orderScratch[p]; + NodeBase node = _layoutOrder[startIndex + localIdx]; + + node.X = startX; + node.Y = y; + + AdjustNode(node); + + rowHeight = node.Height; + + row.Add(node); + _rowIndex[node] = rowIdx; + + p++; + } + + _rows.Add(row); + rowIdx++; + + y += rowHeight + vSpace; } } @@ -416,7 +815,7 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase while (p < count) { int idx = _orderScratch[p]; - NodeBase node = NodeList[idx]; + NodeBase node = _layoutOrder[idx]; float w = node.Width; if (row.Count == 0 || (x + w) <= availableWidth) @@ -446,7 +845,7 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase for (int s = p + 1; s <= end; s++) { int candIdx = _orderScratch[s]; - NodeBase cand = NodeList[candIdx]; + NodeBase cand = _layoutOrder[candIdx]; float cw = cand.Width; if ((x + cw) <= availableWidth) @@ -488,8 +887,7 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase if (row.Count == 0) { int idx = _orderScratch[p]; - NodeBase node = NodeList[idx]; - float w = node.Width; + NodeBase node = _layoutOrder[idx]; node.X = startX; node.Y = y; @@ -517,11 +915,11 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase if (!_requiredHeightDirty) return _requiredHeight; float maxBottom = 0f; - int count = NodeList.Count; + int count = _layoutOrder.Count; for (int i = 0; i < count; i++) { - NodeBase node = NodeList[i]; + NodeBase node = _layoutOrder[i]; float bottom = node.Y + node.Height; if (bottom > maxBottom) maxBottom = bottom; } @@ -615,6 +1013,34 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase _orderScratch = new int[newSize]; } + public IDisposable DeferRecalculateLayout() + { + _deferRecalcDepth++; + return new RecalcDeferToken(this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RequestRecalculateLayout() + { + if (_deferRecalcDepth > 0) + { + _pendingRecalc = true; + return; + } + + RecalculateLayout(); + } + + private void EndDefer() + { + _deferRecalcDepth--; + if (_deferRecalcDepth == 0 && _pendingRecalc) + { + _pendingRecalc = false; + RecalculateLayout(); + } + } + private sealed class RowsReadOnlyView : IReadOnlyList> { private readonly List> _rows; @@ -642,4 +1068,11 @@ public sealed class WrappingGridNode : LayoutListNode where T : NodeBase [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetHashCode(TRef obj) => RuntimeHelpers.GetHashCode(obj); } + + + private readonly struct RecalcDeferToken(WrappingGridNode owner) : IDisposable + where TRef : NodeBase + { + public void Dispose() => owner.EndDefer(); + } } diff --git a/AetherBags/Plugin.cs b/AetherBags/Plugin.cs index 2ad2522..55f36a9 100644 --- a/AetherBags/Plugin.cs +++ b/AetherBags/Plugin.cs @@ -1,14 +1,11 @@ -using System; using System.Numerics; using AetherBags.AddonLifecycles; using AetherBags.Addons; using AetherBags.Commands; using AetherBags.Helpers; using AetherBags.Hooks; +using AetherBags.Inventory; using Dalamud.Plugin; -using Dalamud.Game.Command; -using Dalamud.Hooking; -using FFXIVClientStructs.FFXIV.Client.Game; using KamiToolKit; namespace AetherBags; @@ -50,6 +47,8 @@ public unsafe class Plugin : IDalamudPlugin _commandHandler = new CommandHandler(); + Services.GameInventory.InventoryChanged += InventoryState.OnRawItemAdded; + Services.ClientState.Login += OnLogin; Services.ClientState.Logout += OnLogout; @@ -65,6 +64,8 @@ public unsafe class Plugin : IDalamudPlugin { Util.SaveConfig(System.Config); + Services.GameInventory.InventoryChanged -= InventoryState.OnRawItemAdded; + Services.ClientState.Login -= OnLogin; Services.ClientState.Logout -= OnLogout; @@ -82,16 +83,19 @@ public unsafe class Plugin : IDalamudPlugin private void OnLogin() { System.Config = Util.LoadConfigOrDefault(); + InventoryState.TrackLootedItems = true; - #if DEBUG - System.AddonInventoryWindow.Toggle(); - System.AddonConfigurationWindow.Toggle(); - #endif +#if DEBUG + System.AddonInventoryWindow.Toggle(); + System.AddonConfigurationWindow.Toggle(); +#endif } private void OnLogout(int type, int code) { Util.SaveConfig(System.Config); + InventoryState.TrackLootedItems = false; System.AddonInventoryWindow.Close(); + System.AddonConfigurationWindow.Close(); } } \ No newline at end of file diff --git a/AetherBags/Services.cs b/AetherBags/Services.cs index 7abf28a..bf7a595 100644 --- a/AetherBags/Services.cs +++ b/AetherBags/Services.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using Dalamud.IoC; using Dalamud.Plugin; using Dalamud.Plugin.Services; @@ -15,6 +14,7 @@ public class Services [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 IGameInventory GameInventory { get; 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!;