diff --git a/AetherBags/Addons/AddonInventoryWindow.cs b/AetherBags/Addons/AddonInventoryWindow.cs index af0d6bf..2f9a228 100644 --- a/AetherBags/Addons/AddonInventoryWindow.cs +++ b/AetherBags/Addons/AddonInventoryWindow.cs @@ -25,7 +25,7 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase { InitializeBackgroundDropTarget(); - ScrollableCategories = new ScrollingAreaNode> + ScrollableCategories = new InventoryScrollingAreaNode> { Position = ContentStartPosition, Size = ContentSize, @@ -179,6 +179,8 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase protected override void OnFinalize(AtkUnitBase* addon) { + _lootedCategoryNode?.Dispose(); + System.LootedItemsTracker.OnLootedItemsChanged -= OnLootedItemsChanged; ref var blockingAddonId = ref AgentInventoryContext.Instance()->BlockingAddonId; @@ -189,8 +191,6 @@ public unsafe class AddonInventoryWindow : InventoryAddonBase addon->UnsubscribeAtkArrayData(1, (int)NumberArrayType.Inventory); - _lootedCategoryNode?.Dispose(); - IsSetupComplete = false; base.OnFinalize(addon); } diff --git a/AetherBags/Addons/AddonRetainerWindow.cs b/AetherBags/Addons/AddonRetainerWindow.cs index fc0f5a4..c785fd4 100644 --- a/AetherBags/Addons/AddonRetainerWindow.cs +++ b/AetherBags/Addons/AddonRetainerWindow.cs @@ -38,7 +38,7 @@ public unsafe class AddonRetainerWindow : InventoryAddonBase WindowNode?.AddColor = _tintColor; - ScrollableCategories = new ScrollingAreaNode> + ScrollableCategories = new InventoryScrollingAreaNode> { Position = ContentStartPosition, Size = ContentSize, diff --git a/AetherBags/Addons/AddonSaddleBagWindow.cs b/AetherBags/Addons/AddonSaddleBagWindow.cs index 81faf12..546c50a 100644 --- a/AetherBags/Addons/AddonSaddleBagWindow.cs +++ b/AetherBags/Addons/AddonSaddleBagWindow.cs @@ -31,7 +31,7 @@ public unsafe class AddonSaddleBagWindow : InventoryAddonBase WindowNode?.AddColor = _tintColor; - ScrollableCategories = new ScrollingAreaNode> + ScrollableCategories = new InventoryScrollingAreaNode> { Position = ContentStartPosition, Size = ContentSize, diff --git a/AetherBags/Addons/InventoryAddonBase.cs b/AetherBags/Addons/InventoryAddonBase.cs index 49b971b..9d3400c 100644 --- a/AetherBags/Addons/InventoryAddonBase.cs +++ b/AetherBags/Addons/InventoryAddonBase.cs @@ -29,7 +29,7 @@ public abstract unsafe class InventoryAddonBase : NativeAddon, IInventoryWindow protected readonly HashSet HoverSubscribed = new(); protected DragDropNode BackgroundDropTarget = null!; - protected ScrollingAreaNode> ScrollableCategories = null!; + protected InventoryScrollingAreaNode> ScrollableCategories = null!; protected WrappingGridNode CategoriesNode = null!; protected TextInputWithButtonNode SearchInputNode = null!; protected InventoryFooterNode FooterNode = null!; diff --git a/AetherBags/Nodes/Layout/InventoryScrollingNode.cs b/AetherBags/Nodes/Layout/InventoryScrollingNode.cs new file mode 100644 index 0000000..0692d20 --- /dev/null +++ b/AetherBags/Nodes/Layout/InventoryScrollingNode.cs @@ -0,0 +1,112 @@ +using System.Linq; +using System.Numerics; +using FFXIVClientStructs.FFXIV.Component.GUI; +using KamiToolKit; +using KamiToolKit.Nodes; + +namespace AetherBags.Nodes.Layout; + +/// +/// A copy of KamiToolKit's ScrollingAreaNode with ContentAreaClipNode changed from +/// SimpleComponentNode to ResNode, to prevent the native AtkDragDropManager from +/// treating the clip boundary as a component node (which blocks drag-to-hotbar). +/// According to Kami it is possible that this may or may not leak. +/// We will eventually change this later. +/// +public unsafe class InventoryScrollingAreaNode : SimpleComponentNode where T : NodeBase, new() { + + public readonly ResNode ContentAreaClipNode; + public readonly T ContentAreaNode; + public readonly ScrollBarNode ScrollBarNode; + public readonly CollisionNode ScrollingCollisionNode; + + public InventoryScrollingAreaNode() { + ScrollingCollisionNode = new CollisionNode(); + ScrollingCollisionNode.AttachNode(this); + + ContentAreaClipNode = new ResNode { + NodeFlags = NodeFlags.Clip | NodeFlags.EmitsEvents | NodeFlags.Visible, + }; + ContentAreaClipNode.AttachNode(this); + + ContentAreaNode = new T(); + ContentAreaNode.AttachNode(ContentAreaClipNode); + + ScrollBarNode = new ScrollBarNode { + ContentNode = ContentAreaNode, + ContentCollisionNode = ScrollingCollisionNode, + HideWhenDisabled = true, + }; + ScrollBarNode.AttachNode(this); + + AtkResNode* clipNode = ContentAreaClipNode; + AtkResNode* contentNode = ContentAreaNode; + + clipNode->AtkEventManager.RegisterEvent( + AtkEventType.MouseWheel, + 5, + null, + ScrollingCollisionNode, + ScrollBarNode, + false); + + ScrollingCollisionNode.Node->AtkEventManager.RegisterEvent( + AtkEventType.MouseWheel, + 5, + null, + ScrollingCollisionNode, + ScrollBarNode, + false); + + contentNode->AtkEventManager.RegisterEvent( + AtkEventType.MouseWheel, + 5, + null, + ScrollingCollisionNode, + ScrollBarNode, + false); + } + + public virtual T ContentNode => ContentAreaNode; + + public int ScrollPosition { + get => ScrollBarNode.ScrollPosition; + set => ScrollBarNode.ScrollPosition = value; + } + + public int ScrollSpeed { + get => ScrollBarNode.ScrollSpeed; + set => ScrollBarNode.ScrollSpeed = value; + } + + public required float ContentHeight { + get => ContentAreaNode.Height; + set { + ContentAreaNode.Height = value; + ScrollBarNode.UpdateScrollParams(); + } + } + + public bool AutoHideScrollBar { + get => ScrollBarNode.HideWhenDisabled; + set => ScrollBarNode.HideWhenDisabled = value; + } + + protected override void OnSizeChanged() { + base.OnSizeChanged(); + + ContentAreaNode.Width = Width - 16.0f; + ScrollingCollisionNode.Size = new Vector2(Width - 16.0f, Height); + ContentAreaClipNode.Size = new Vector2(Width - 16.0f, Height); + ScrollBarNode.Size = new Vector2(8.0f, Height); + ScrollBarNode.UpdateScrollParams(); + + ScrollBarNode.X = Width - 8.0f; + } + + public void FitToContentHeight() { + if (ContentNode is LayoutListNode layoutNode) { + ContentHeight = layoutNode.Nodes.Sum(node => node.IsVisible ? node.Height + layoutNode.ItemSpacing : 0.0f) + layoutNode.FirstItemSpacing; + } + } +} \ No newline at end of file