diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index f894f64..ebb0d5b 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -4,8 +4,6 @@ using AetherBags.Inventory.State; using AetherBags.Nodes.Input; using AetherBags.Nodes.Inventory; using AetherBags.Nodes.Layout; -using Dalamud.Game.Addon.Lifecycle; -using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -35,36 +33,26 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase }; CategoriesNode.AttachNode(this); - var size = new Vector2(addon->Size.X / 2.0f, 28.0f); - - var header = addon->WindowHeaderCollisionNode; - - float headerX = header->X; - float headerY = header->Y; - float headerW = header->Width; - float headerH = header->Height; - - float x = headerX + (headerW - size.X) * 0.5f; - float y = headerY + (headerH - size.Y) * 0.5f; + var header = CalculateHeaderLayout(addon); _notificationNode = new InventoryNotificationNode { Position = new Vector2(WindowNode!.X - 4f, WindowNode!.Y - 32f), - Size = new Vector2(headerW, 28f), + Size = new Vector2(header.HeaderWidth, 28f), }; _notificationNode.AttachNode(this); SearchInputNode = new TextInputWithHintNode { - Position = new Vector2(x, y), - Size = size, + Position = header.SearchPosition, + Size = header.SearchSize, OnInputReceived = _ => RefreshCategoriesCore(autosize: false), }; SearchInputNode.AttachNode(this); SettingsButtonNode = new CircleButtonNode { - Position = new Vector2(headerW - 48f, y), + Position = new Vector2(header.HeaderWidth - SettingsButtonOffset, header.HeaderY), Size = new Vector2(28f), Icon = ButtonIcon.GearCog, OnClick = System.AddonConfigurationWindow.Toggle @@ -90,34 +78,12 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase base.OnSetup(addon); } - protected override void OnUpdate(AtkUnitBase* addon) - { - if (RefreshQueued) - { - bool doAutosize = RefreshAutosizeQueued; - RefreshQueued = false; - RefreshAutosizeQueued = false; - - RefreshCategoriesCore(doAutosize); - } - - base.OnUpdate(addon); - } - public void ManualCurrencyRefresh() { if (!Services.ClientState.IsLoggedIn) return; FooterNode.RefreshCurrencies(); } - protected override void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) - { - base.OnRequestedUpdate(addon, numberArrayData, stringArrayData); - - _inventoryState.RefreshFromGame(); - RefreshCategoriesCore(autosize: true); - } - public void SetNotification(InventoryNotificationInfo info) { Services.Framework.RunOnTick(() => @@ -126,15 +92,6 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase }, delayTicks: 1); } - public void SetSearchText(string searchText) - { - Services.Framework.RunOnTick(() => - { - if (IsOpen) SearchInputNode.SearchString = searchText; - RefreshCategoriesCore(autosize: true); - }, delayTicks: 1); - } - protected override void OnFinalize(AtkUnitBase* addon) { ref var blockingAddonId = ref AgentInventoryContext.Instance()->BlockingAddonId; diff --git a/AetherBags/Addons/AddonRetainerWindow.cs b/AetherBags/Addons/AddonRetainerWindow.cs index 15e3eee..7f0615b 100644 --- a/AetherBags/Addons/AddonRetainerWindow.cs +++ b/AetherBags/Addons/AddonRetainerWindow.cs @@ -1,11 +1,10 @@ +using System.Linq; using System.Numerics; using AetherBags.Inventory; using AetherBags.Inventory.State; using AetherBags.Nodes.Input; using AetherBags.Nodes.Inventory; using AetherBags.Nodes.Layout; -using Dalamud.Game.Addon.Lifecycle; -using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -31,6 +30,8 @@ public unsafe class AddonRetainerWindow : InventoryAddonBase protected override float MinWindowWidth => 400; protected override float MaxWindowWidth => 700; + private readonly string[] _retainerAddonNames = { "InventoryRetainer", "InventoryRetainerLarge" }; + protected override void OnSetup(AtkUnitBase* addon) { InitializeBackgroundDropTarget(); @@ -48,29 +49,19 @@ public unsafe class AddonRetainerWindow : InventoryAddonBase }; CategoriesNode.AttachNode(this); - var size = new Vector2(addon->Size.X / 2.0f, 28.0f); - - var header = addon->WindowHeaderCollisionNode; - - float headerX = header->X; - float headerY = header->Y; - float headerW = header->Width; - float headerH = header->Height; - - float x = headerX + (headerW - size.X) * 0.5f; - float y = headerY + (headerH - size.Y) * 0.5f; + var header = CalculateHeaderLayout(addon); SearchInputNode = new TextInputWithHintNode { - Position = new Vector2(x, y), - Size = size, + Position = header.SearchPosition, + Size = header.SearchSize, OnInputReceived = _ => RefreshCategoriesCore(autosize: false), }; SearchInputNode.AttachNode(this); SettingsButtonNode = new CircleButtonNode { - Position = new Vector2(headerW - 48f, y), + Position = new Vector2(header.HeaderWidth - SettingsButtonOffset, header.HeaderY), Size = new Vector2(28f), Icon = ButtonIcon.GearCog, OnClick = System.AddonConfigurationWindow.Toggle @@ -152,18 +143,23 @@ public unsafe class AddonRetainerWindow : InventoryAddonBase SlotCounterNode.Position = new Vector2(contentSize.X - 80f, footerY); } - protected override void OnUpdate(AtkUnitBase* addon) + private void CloseRetainerWindows() { - if (RefreshQueued) + var manager = RaptureAtkUnitManager.Instance(); + foreach (var name in _retainerAddonNames) { - bool doAutosize = RefreshAutosizeQueued; - RefreshQueued = false; - RefreshAutosizeQueued = false; - - RefreshCategoriesCore(doAutosize); + var addon = manager->GetAddonByName(name); + if (addon != null) + { + addon->IsVisible = true; + addon->Close(true); + } } + } - base.OnUpdate(addon); + private bool IsAnyRetainerWindowLoaded() + { + return _retainerAddonNames.Any(name => RaptureAtkUnitManager.Instance()->GetAddonByName(name) != null); } protected override void OnShow(AtkUnitBase* addon) @@ -175,48 +171,16 @@ public unsafe class AddonRetainerWindow : InventoryAddonBase private void OnEntrustDuplicates() { - // TODO: Implement checking if the retainer bag is def open + if (!IsAnyRetainerWindowLoaded()) return; var agent = AgentModule.Instance()->GetAgentByInternalId(AgentId.Retainer); agent->SendCommand(0, [0]); } - protected override void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) - { - base.OnRequestedUpdate(addon, numberArrayData, stringArrayData); - - _inventoryState.RefreshFromGame(); - RefreshCategoriesCore(autosize: true); - } - - public void SetSearchText(string searchText) - { - Services.Framework.RunOnTick(() => - { - if (IsOpen) SearchInputNode.SearchString = searchText; - RefreshCategoriesCore(autosize: true); - }, delayTicks: 1); - } - protected override void OnFinalize(AtkUnitBase* addon) { _isSetupComplete = false; - if (System.Config.General.HideGameRetainer) - { - var retainerAddon = RaptureAtkUnitManager.Instance()->GetAddonByName("InventoryRetainer"); - if (retainerAddon != null) - { - retainerAddon->IsVisible = true; - retainerAddon->Close(true); - } - - retainerAddon = RaptureAtkUnitManager.Instance()->GetAddonByName("InventoryRetainerLarge"); - if (retainerAddon != null) - { - retainerAddon->IsVisible = true; - retainerAddon->Close(true); - } - } + CloseRetainerWindows(); base.OnFinalize(addon); } diff --git a/AetherBags/Addons/AddonSaddleBagWindow.cs b/AetherBags/Addons/AddonSaddleBagWindow.cs index 9ee5036..336be21 100644 --- a/AetherBags/Addons/AddonSaddleBagWindow.cs +++ b/AetherBags/Addons/AddonSaddleBagWindow.cs @@ -42,29 +42,19 @@ public unsafe class AddonSaddleBagWindow : InventoryAddonBase }; CategoriesNode.AttachNode(this); - var size = new Vector2(addon->Size.X / 2.0f, 28.0f); - - var header = addon->WindowHeaderCollisionNode; - - float headerX = header->X; - float headerY = header->Y; - float headerW = header->Width; - float headerH = header->Height; - - float x = headerX + (headerW - size.X) * 0.5f; - float y = headerY + (headerH - size.Y) * 0.5f; + var header = CalculateHeaderLayout(addon); SearchInputNode = new TextInputWithHintNode { - Position = new Vector2(x, y), - Size = size, + Position = header.SearchPosition, + Size = header.SearchSize, OnInputReceived = _ => RefreshCategoriesCore(autosize: false), }; SearchInputNode.AttachNode(this); SettingsButtonNode = new CircleButtonNode { - Position = new Vector2(headerW - 48f, y), + Position = new Vector2(header.HeaderWidth - SettingsButtonOffset, header.HeaderY), Size = new Vector2(28f), AddColor = _tintColor, Icon = ButtonIcon.GearCog, @@ -80,7 +70,7 @@ public unsafe class AddonSaddleBagWindow : InventoryAddonBase FontType = FontType.MiedingerMed, TextFlags = TextFlags.Glare, TextColor = ColorHelper.GetColor(50), - TextOutlineColor = ColorHelper.GetColor(32) // Could also be Color 65 + TextOutlineColor = ColorHelper.GetColor(32) }; _slotCounterNode.AttachNode(this); SlotCounterNode = _slotCounterNode; @@ -106,37 +96,6 @@ public unsafe class AddonSaddleBagWindow : InventoryAddonBase base.RefreshCategoriesCore(autosize); } - protected override void OnUpdate(AtkUnitBase* addon) - { - if (RefreshQueued) - { - bool doAutosize = RefreshAutosizeQueued; - RefreshQueued = false; - RefreshAutosizeQueued = false; - - RefreshCategoriesCore(doAutosize); - } - - base.OnUpdate(addon); - } - - protected override void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) - { - base.OnRequestedUpdate(addon, numberArrayData, stringArrayData); - - _inventoryState.RefreshFromGame(); - RefreshCategoriesCore(autosize: true); - } - - public void SetSearchText(string searchText) - { - Services.Framework.RunOnTick(() => - { - if (IsOpen) SearchInputNode.SearchString = searchText; - RefreshCategoriesCore(autosize: true); - }, delayTicks: 1); - } - protected override void OnFinalize(AtkUnitBase* addon) { _isSetupComplete = false; diff --git a/AetherBags/Addons/IInventoryWindow.cs b/AetherBags/Addons/IInventoryWindow.cs new file mode 100644 index 0000000..1f76fbf --- /dev/null +++ b/AetherBags/Addons/IInventoryWindow.cs @@ -0,0 +1,10 @@ +namespace AetherBags.Addons; + +public interface IInventoryWindow +{ + bool IsOpen { get; } + void Toggle(); + void Close(); + void ManualRefresh(); + void SetSearchText(string searchText); +} \ No newline at end of file diff --git a/AetherBags/Addons/InventoryAddonBase.cs b/AetherBags/Addons/InventoryAddonBase.cs index f628f7b..4f84c63 100644 --- a/AetherBags/Addons/InventoryAddonBase.cs +++ b/AetherBags/Addons/InventoryAddonBase.cs @@ -18,7 +18,7 @@ using KamiToolKit.Nodes; namespace AetherBags.Addons; -public abstract unsafe class InventoryAddonBase : NativeAddon +public abstract unsafe class InventoryAddonBase : NativeAddon, IInventoryWindow { protected readonly InventoryCategoryHoverCoordinator HoverCoordinator = new(); protected readonly InventoryCategoryPinCoordinator PinCoordinator = new(); @@ -41,6 +41,7 @@ public abstract unsafe class InventoryAddonBase : NativeAddon protected const float ItemPadding = 4; protected const float FooterHeight = 28f; protected const float FooterTopSpacing = 4f; + protected const float SettingsButtonOffset = 48f; protected bool RefreshQueued; protected bool RefreshAutosizeQueued; @@ -72,6 +73,15 @@ public abstract unsafe class InventoryAddonBase : NativeAddon } } + public virtual void SetSearchText(string searchText) + { + Services.Framework.RunOnTick(() => + { + if (IsOpen) SearchInputNode.SearchString = searchText; + RefreshCategoriesCore(autosize: true); + }, delayTicks: 1); + } + public void RefreshFromLifecycle() { if (!_isSetupComplete) return; @@ -135,6 +145,34 @@ public abstract unsafe class InventoryAddonBase : NativeAddon } } + protected readonly struct HeaderLayout + { + public Vector2 SearchPosition { get; init; } + public Vector2 SearchSize { get; init; } + public float HeaderWidth { get; init; } + public float HeaderY { get; init; } + } + + protected HeaderLayout CalculateHeaderLayout(AtkUnitBase* addon) + { + var size = new Vector2(addon->Size.X / 2.0f, 28.0f); + var header = addon->WindowHeaderCollisionNode; + + float headerW = header->Width; + float headerH = header->Height; + + float x = header->X + (headerW - size.X) * 0.5f; + float y = header->Y + (headerH - size.Y) * 0.5f; + + return new HeaderLayout + { + SearchPosition = new Vector2(x, y), + SearchSize = size, + HeaderWidth = headerW, + HeaderY = y, + }; + } + protected void InitializeBackgroundDropTarget() { BackgroundDropTarget = new DragDropNode @@ -306,6 +344,28 @@ public abstract unsafe class InventoryAddonBase : NativeAddon protected void ResizeWindow(float width, float height) => ResizeWindow(width, height, recalcLayout: true); + protected override void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) + { + base.OnRequestedUpdate(addon, numberArrayData, stringArrayData); + + InventoryState.RefreshFromGame(); + RefreshCategoriesCore(autosize: true); + } + + protected override void OnUpdate(AtkUnitBase* addon) + { + if (RefreshQueued) + { + bool doAutosize = RefreshAutosizeQueued; + RefreshQueued = false; + RefreshAutosizeQueued = false; + + RefreshCategoriesCore(doAutosize); + } + + base.OnUpdate(addon); + } + protected override void OnFinalize(AtkUnitBase* addon) { HoverSubscribed.Clear(); diff --git a/AetherBags/Inventory/InventoryOrchestrator.cs b/AetherBags/Inventory/InventoryOrchestrator.cs index 73f9203..a54dab6 100644 --- a/AetherBags/Inventory/InventoryOrchestrator.cs +++ b/AetherBags/Inventory/InventoryOrchestrator.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using AetherBags.Addons; using AetherBags.Inventory.Context; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -9,32 +11,33 @@ public static unsafe class InventoryOrchestrator public static void RefreshAll(bool updateMaps = true) { - // 1. Update the mapping data (Context menus / Visual slots) if (updateMaps) { InventoryContextState.RefreshMaps(); InventoryContextState.RefreshBlockedSlots(); } - // 2. Fetch the current context (Are we selling? Trading? Talking to a retainer?) var agent = AgentInventory.Instance(); var contextId = agent != null ? agent->OpenTitleId : 0; var notification = NotificationState.GetNotificationInfo(contextId); - // 3. Trigger UI refreshes Services.Framework.RunOnTick(() => { if (System.AddonInventoryWindow.IsOpen) - { System.AddonInventoryWindow.SetNotification(notification!); - System.AddonInventoryWindow.ManualRefresh(); + + foreach (var window in GetAllWindows()) + { + if (window.IsOpen) + window.ManualRefresh(); } - - if (System.AddonSaddleBagWindow.IsOpen) - System.AddonSaddleBagWindow.ManualRefresh(); - - if (System.AddonRetainerWindow.IsOpen) - System.AddonRetainerWindow.ManualRefresh(); }); } + + private static IEnumerable GetAllWindows() + { + yield return System.AddonInventoryWindow; + yield return System.AddonSaddleBagWindow; + yield return System.AddonRetainerWindow; + } } \ No newline at end of file diff --git a/AetherBags/Nodes/Inventory/InventoryFooterNode.cs b/AetherBags/Nodes/Inventory/InventoryFooterNode.cs index 5c9747e..5649a28 100644 --- a/AetherBags/Nodes/Inventory/InventoryFooterNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryFooterNode.cs @@ -25,7 +25,7 @@ public sealed class InventoryFooterNode : SimpleComponentNode FontType = FontType.MiedingerMed, TextFlags = TextFlags.Glare, TextColor = ColorHelper.GetColor(50), - TextOutlineColor = ColorHelper.GetColor(32) // Could also be Color 65 + TextOutlineColor = ColorHelper.GetColor(32) }; _slotAmountTextNode.AttachNode(this); diff --git a/AetherBags/Plugin.cs b/AetherBags/Plugin.cs index d39c2d6..827637d 100644 --- a/AetherBags/Plugin.cs +++ b/AetherBags/Plugin.cs @@ -16,11 +16,9 @@ namespace AetherBags; public unsafe class Plugin : IDalamudPlugin { - private static string HelpDescription => "Opens your inventory."; - private readonly CommandHandler _commandHandler; private readonly InventoryHooks _inventoryHooks; - private readonly InventoryLifecycles inventoryLifecycles; + private readonly InventoryLifecycles _inventoryLifecycles; public Plugin(IDalamudPluginInterface pluginInterface) { @@ -66,8 +64,6 @@ public unsafe class Plugin : IDalamudPlugin _commandHandler = new CommandHandler(); - // Services.GameInventory.InventoryChanged += InventoryState.OnRawItemAdded; - Services.ClientState.Login += OnLogin; Services.ClientState.Logout += OnLogout; @@ -76,7 +72,7 @@ public unsafe class Plugin : IDalamudPlugin } _inventoryHooks = new InventoryHooks(); - inventoryLifecycles = new InventoryLifecycles(); + _inventoryLifecycles = new InventoryLifecycles(); } public void Dispose() @@ -96,7 +92,7 @@ public unsafe class Plugin : IDalamudPlugin KamiToolKitLibrary.Dispose(); _inventoryHooks.Dispose(); - inventoryLifecycles.Dispose(); + _inventoryLifecycles.Dispose(); } private void OnLogin() diff --git a/AetherBags/Services.cs b/AetherBags/Services.cs index fb13d0e..71fb89e 100644 --- a/AetherBags/Services.cs +++ b/AetherBags/Services.cs @@ -21,7 +21,6 @@ public class Services [PluginService] public static IPluginLog Logger { get; private set; } = null!; [PluginService] public static INotificationManager NotificationManager { get; private set; } = null!; [PluginService] public static IObjectTable ObjectTable { get; private set; } = null!; - // TODO: Remove cause temp [PluginService] public static ISigScanner SigScanner { get; private set; } = null!; [PluginService] public static IGameInteropProvider GameInteropProvider { get; private set; } = null!; } \ No newline at end of file