Pinning, Hoisting, Recently Lotted
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using AetherBags.Nodes.Layout;
|
||||
using AetherBags.Nodes.Layout;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
@@ -6,76 +6,94 @@ public sealed class InventoryCategoryHoverCoordinator
|
||||
{
|
||||
private InventoryCategoryNode? _active;
|
||||
private int _activeRowIndex = -1;
|
||||
private bool _isProcessing;
|
||||
|
||||
public void OnCategoryHoverChanged(
|
||||
WrappingGridNode<InventoryCategoryNode> grid,
|
||||
WrappingGridNode<InventoryCategoryNodeBase> grid,
|
||||
InventoryCategoryNode source,
|
||||
bool hovering)
|
||||
{
|
||||
grid.RecalculateLayout();
|
||||
if (_isProcessing)
|
||||
return;
|
||||
|
||||
if (hovering)
|
||||
try
|
||||
{
|
||||
_active = source;
|
||||
_isProcessing = true;
|
||||
grid.RecalculateLayout();
|
||||
|
||||
if (!grid.TryGetRowIndex(source, out _activeRowIndex))
|
||||
if (hovering)
|
||||
{
|
||||
SuppressAllExcept(grid, source);
|
||||
_active = source;
|
||||
|
||||
if (!grid.TryGetRowIndex(source, out _activeRowIndex))
|
||||
{
|
||||
SuppressAllExcept(grid, source);
|
||||
source.SetHeaderSuppressed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearAll(grid);
|
||||
|
||||
var row = grid.Rows[_activeRowIndex];
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
if (row[i] is InventoryCategoryNode cat && !ReferenceEquals(cat, source))
|
||||
cat.SetHeaderSuppressed(true);
|
||||
}
|
||||
|
||||
source.SetHeaderSuppressed(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearAll(grid);
|
||||
if (!ReferenceEquals(_active, source))
|
||||
return;
|
||||
|
||||
var row = grid.Rows[_activeRowIndex];
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
_active = null;
|
||||
|
||||
if (_activeRowIndex >= 0 && _activeRowIndex < grid.Rows.Count)
|
||||
{
|
||||
if (row[i] is InventoryCategoryNode cat && !ReferenceEquals(cat, source))
|
||||
cat.SetHeaderSuppressed(true);
|
||||
var row = grid.Rows[_activeRowIndex];
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
if (row[i] is InventoryCategoryNode cat)
|
||||
cat.SetHeaderSuppressed(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearAll(grid);
|
||||
}
|
||||
|
||||
source.SetHeaderSuppressed(false);
|
||||
return;
|
||||
_activeRowIndex = -1;
|
||||
}
|
||||
|
||||
if (!ReferenceEquals(_active, source))
|
||||
return;
|
||||
|
||||
_active = null;
|
||||
|
||||
if (_activeRowIndex >= 0 && _activeRowIndex < grid.Rows.Count)
|
||||
finally
|
||||
{
|
||||
var row = grid.Rows[_activeRowIndex];
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
if (row[i] is InventoryCategoryNode cat)
|
||||
cat.SetHeaderSuppressed(false);
|
||||
}
|
||||
_isProcessing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearAll(grid);
|
||||
}
|
||||
|
||||
_activeRowIndex = -1;
|
||||
}
|
||||
|
||||
public void ResetAll(WrappingGridNode<InventoryCategoryNode> grid)
|
||||
public void ResetAll(WrappingGridNode<InventoryCategoryNodeBase> grid)
|
||||
{
|
||||
_active = null;
|
||||
_activeRowIndex = -1;
|
||||
ClearAll(grid);
|
||||
}
|
||||
|
||||
private static void ClearAll(WrappingGridNode<InventoryCategoryNode> grid)
|
||||
private static void ClearAll(WrappingGridNode<InventoryCategoryNodeBase> grid)
|
||||
{
|
||||
foreach (var cat in grid.GetNodes<InventoryCategoryNode>())
|
||||
cat.SetHeaderSuppressed(false);
|
||||
foreach (var node in grid.GetNodes<InventoryCategoryNodeBase>())
|
||||
{
|
||||
if (node is InventoryCategoryNode cat)
|
||||
cat.SetHeaderSuppressed(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SuppressAllExcept(WrappingGridNode<InventoryCategoryNode> grid, InventoryCategoryNode source)
|
||||
private static void SuppressAllExcept(WrappingGridNode<InventoryCategoryNodeBase> grid, InventoryCategoryNode source)
|
||||
{
|
||||
foreach (var cat in grid.GetNodes<InventoryCategoryNode>())
|
||||
cat.SetHeaderSuppressed(!ReferenceEquals(cat, source));
|
||||
foreach (var node in grid.GetNodes<InventoryCategoryNodeBase>())
|
||||
{
|
||||
if (node is InventoryCategoryNode cat)
|
||||
cat.SetHeaderSuppressed(!ReferenceEquals(cat, source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using AetherBags.Helpers;
|
||||
using AetherBags.Hooks;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Inventory.Categories;
|
||||
using AetherBags.Inventory.Items;
|
||||
@@ -9,15 +8,17 @@ using AetherBags.Nodes.Layout;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
public class InventoryCategoryNode : SimpleComponentNode
|
||||
public class InventoryCategoryNode : InventoryCategoryNodeBase
|
||||
{
|
||||
private const uint CategoryNodeKeyBase = 0x10000000;
|
||||
|
||||
public override uint Key => CategoryNodeKeyBase | CategorizedInventory.Key;
|
||||
private readonly TextNode _categoryNameTextNode;
|
||||
private readonly HybridDirectionalFlexNode<DragDropNode> _itemGridNode;
|
||||
|
||||
@@ -109,7 +110,7 @@ public class InventoryCategoryNode : SimpleComponentNode
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPinnedInConfig => CategorizedInventory.Category?.IsPinned ?? false;
|
||||
public override bool IsPinnedInConfig => CategorizedInventory.Category?.IsPinned ?? false;
|
||||
|
||||
public void BeginHeaderHover()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for category-like nodes that can be displayed in the inventory grid.
|
||||
/// Used to allow both regular categories and special categories (like looted items) to be hoisted/pinned.
|
||||
/// </summary>
|
||||
public abstract class InventoryCategoryNodeBase : SimpleComponentNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique key for this category, used for sync operations.
|
||||
/// </summary>
|
||||
public abstract uint Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this category should be pinned in the layout.
|
||||
/// </summary>
|
||||
public virtual bool IsPinnedInConfig => false;
|
||||
}
|
||||
@@ -4,13 +4,13 @@ namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
public sealed class InventoryCategoryPinCoordinator
|
||||
{
|
||||
public bool ApplyPinnedStates(WrappingGridNode<InventoryCategoryNode> grid)
|
||||
public bool ApplyPinnedStates(WrappingGridNode<InventoryCategoryNodeBase> grid)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
using (grid.DeferRecalculateLayout())
|
||||
{
|
||||
foreach (var node in grid.GetNodes<InventoryCategoryNode>())
|
||||
foreach (var node in grid.GetNodes<InventoryCategoryNodeBase>())
|
||||
{
|
||||
bool shouldBePinned = node.IsPinnedInConfig;
|
||||
|
||||
@@ -38,7 +38,7 @@ public sealed class InventoryCategoryPinCoordinator
|
||||
return changed;
|
||||
}
|
||||
|
||||
public bool PrunePinnedNotInGrid(WrappingGridNode<InventoryCategoryNode> grid)
|
||||
public bool PrunePinnedNotInGrid(WrappingGridNode<InventoryCategoryNodeBase> grid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Numerics;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Inventory.Items;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AetherBags.Currency;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Inventory.State;
|
||||
using AetherBags.Nodes.Currency;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
using static AetherBags.Inventory.State.InventoryStateBase;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
public sealed class InventoryFooterNode : SimpleComponentNode
|
||||
@@ -44,7 +44,7 @@ public sealed class InventoryFooterNode : SimpleComponentNode
|
||||
{
|
||||
_currencyListNode.IsVisible = System.Config.Currency.Enabled;
|
||||
|
||||
IReadOnlyList<CurrencyInfo> currencyInfoList = InventoryState.GetCurrencyInfoList([1, 28, 0xFFFF_FFFE, 0xFFFF_FFFD]);
|
||||
IReadOnlyList<CurrencyInfo> currencyInfoList = GetCurrencyInfoList([1, 28, 0xFFFF_FFFE, 0xFFFF_FFFD]);
|
||||
_currencyListNode.SyncWithListDataByKey<CurrencyInfo, CurrencyNode, uint>(
|
||||
dataList: currencyInfoList,
|
||||
getKeyFromData: currencyInfo => currencyInfo.ItemId,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Numerics;
|
||||
using AetherBags.Inventory;
|
||||
using AetherBags.Inventory.Context;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using AetherBags.Inventory.Items;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// A display-only item node for looted items. Not draggable, but shows tooltip and can be dismissed.
|
||||
/// </summary>
|
||||
public unsafe class LootedItemDisplayNode : SimpleComponentNode
|
||||
{
|
||||
private readonly IconNode _iconNode;
|
||||
private readonly TextNode _quantityTextNode;
|
||||
private readonly ResNode _collisionNode;
|
||||
|
||||
public Action<LootedItemDisplayNode>? OnDismiss { get; set; }
|
||||
|
||||
public LootedItemDisplayNode()
|
||||
{
|
||||
Size = new Vector2(42, 46);
|
||||
|
||||
_iconNode = new IconNode
|
||||
{
|
||||
Position = new Vector2(0, 0),
|
||||
Size = new Vector2(42, 46),
|
||||
NodeFlags = NodeFlags.Visible | NodeFlags.Enabled,
|
||||
};
|
||||
_iconNode.AttachNode(this);
|
||||
|
||||
_quantityTextNode = new TextNode
|
||||
{
|
||||
Size = new Vector2(40.0f, 12.0f),
|
||||
Position = new Vector2(4.0f, 34.0f),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents,
|
||||
Color = ColorHelper.GetColor(50),
|
||||
TextOutlineColor = ColorHelper.GetColor(51),
|
||||
TextFlags = TextFlags.Edge,
|
||||
AlignmentType = AlignmentType.Right,
|
||||
};
|
||||
_quantityTextNode.AttachNode(this);
|
||||
|
||||
_collisionNode = new ResNode
|
||||
{
|
||||
Size = new Vector2(42, 46),
|
||||
NodeFlags = NodeFlags.Enabled | NodeFlags.EmitsEvents | NodeFlags.HasCollision,
|
||||
};
|
||||
_collisionNode.AddEvent(AtkEventType.MouseOver, OnMouseOver);
|
||||
_collisionNode.AddEvent(AtkEventType.MouseOut, OnMouseOut);
|
||||
_collisionNode.AddEvent(AtkEventType.MouseClick, OnMouseClick);
|
||||
_collisionNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public LootedItemInfo LootedItem { get; private set; } = null!;
|
||||
|
||||
public void SetLootedItem(LootedItemInfo lootedItem)
|
||||
{
|
||||
LootedItem = lootedItem;
|
||||
var item = lootedItem.Item;
|
||||
_iconNode.IconId = item.IconId;
|
||||
_quantityTextNode.String = lootedItem.Quantity > 1 ? lootedItem.Quantity.ToString() : string.Empty;
|
||||
}
|
||||
|
||||
private void OnMouseOver(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData)
|
||||
{
|
||||
var item = LootedItem.Item;
|
||||
_collisionNode.ShowInventoryItemTooltip(item.Container, item.Slot);
|
||||
}
|
||||
|
||||
private void OnMouseOut(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData)
|
||||
{
|
||||
ushort addonId = RaptureAtkUnitManager.Instance()->GetAddonByNode(_collisionNode)->Id;
|
||||
AtkStage.Instance()->TooltipManager.HideTooltip(addonId);
|
||||
}
|
||||
|
||||
private void OnMouseClick(AtkEventListener* thisPtr, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, AtkEventData* atkEventData)
|
||||
{
|
||||
if (!atkEventData->IsLeftClick) return;
|
||||
OnDismiss?.Invoke(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AetherBags.Inventory.Items;
|
||||
using AetherBags.Nodes.Layout;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
/// <summary>
|
||||
/// A special category node for displaying recently looted items.
|
||||
/// Items are not draggable but can be dismissed individually or cleared entirely.
|
||||
/// </summary>
|
||||
public class LootedItemsCategoryNode : InventoryCategoryNodeBase
|
||||
{
|
||||
private const uint LootedCategoryKey = 0x20000001;
|
||||
|
||||
public override uint Key => LootedCategoryKey;
|
||||
private readonly TextNode _headerTextNode;
|
||||
private readonly CircleButtonNode _clearButton;
|
||||
private readonly HybridDirectionalFlexNode<LootedItemDisplayNode> _itemGridNode;
|
||||
|
||||
private const float HeaderHeight = 20;
|
||||
private const float ClearButtonSize = 20;
|
||||
private const float MinWidth = 100;
|
||||
|
||||
private IReadOnlyList<LootedItemInfo> _lootedItems = Array.Empty<LootedItemInfo>();
|
||||
|
||||
private int _hoverRefs;
|
||||
private bool _headerExpanded;
|
||||
private float _baseHeaderWidth = 96f;
|
||||
private string _fullHeaderText = "Recently Looted";
|
||||
|
||||
public event Action<LootedItemsCategoryNode, bool>? HeaderHoverChanged;
|
||||
public Action<int>? OnDismissItem { get; set; }
|
||||
public Action? OnClearAll { get; set; }
|
||||
|
||||
public int ItemsPerLine
|
||||
{
|
||||
get => _itemGridNode.ItemsPerLine;
|
||||
set
|
||||
{
|
||||
if (_itemGridNode.ItemsPerLine == value) return;
|
||||
_itemGridNode.ItemsPerLine = value;
|
||||
RecalculateSize();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasItems => _lootedItems.Count > 0;
|
||||
|
||||
public LootedItemsCategoryNode()
|
||||
{
|
||||
_headerTextNode = new TextNode
|
||||
{
|
||||
Position = Vector2.Zero,
|
||||
Size = new Vector2(96, HeaderHeight),
|
||||
AlignmentType = AlignmentType.Left,
|
||||
String = "Recently Looted",
|
||||
TextFlags = TextFlags.OverflowHidden | TextFlags.Ellipsis,
|
||||
TextColor = new Vector4(0.9f, 0.8f, 0.5f, 1.0f), // Gold-ish color
|
||||
};
|
||||
|
||||
_headerTextNode.AddEvent(AtkEventType.MouseOver, BeginHeaderHover);
|
||||
_headerTextNode.AddEvent(AtkEventType.MouseOut, EndHeaderHover);
|
||||
|
||||
_headerTextNode.TextFlags |= TextFlags.OverflowHidden | TextFlags.Ellipsis;
|
||||
_headerTextNode.TextFlags &= ~(TextFlags.WordWrap | TextFlags.MultiLine);
|
||||
|
||||
_headerTextNode.AddFlags(NodeFlags.EmitsEvents | NodeFlags.HasCollision);
|
||||
_headerTextNode.AttachNode(this);
|
||||
|
||||
_clearButton = new CircleButtonNode
|
||||
{
|
||||
Size = new Vector2(ClearButtonSize),
|
||||
Icon = ButtonIcon.CrossSmall,
|
||||
OnClick = () => OnClearAll?.Invoke(),
|
||||
};
|
||||
_clearButton.AttachNode(this);
|
||||
|
||||
_itemGridNode = new HybridDirectionalFlexNode<LootedItemDisplayNode>
|
||||
{
|
||||
Position = new Vector2(0, HeaderHeight),
|
||||
Size = new Vector2(240, 92),
|
||||
FillRowsFirst = true,
|
||||
ItemsPerLine = 10,
|
||||
HorizontalPadding = 5,
|
||||
VerticalPadding = 2,
|
||||
};
|
||||
_itemGridNode.NodeFlags |= NodeFlags.EmitsEvents;
|
||||
_itemGridNode.AttachNode(this);
|
||||
|
||||
RecalculateSize();
|
||||
}
|
||||
|
||||
public void UpdateLootedItems(IReadOnlyList<LootedItemInfo> lootedItems)
|
||||
{
|
||||
_lootedItems = lootedItems;
|
||||
UpdateHeaderText();
|
||||
SyncItemGrid();
|
||||
RecalculateSize();
|
||||
}
|
||||
|
||||
private void UpdateHeaderText()
|
||||
{
|
||||
_fullHeaderText = _lootedItems.Count > 0
|
||||
? $"Recently Looted ({_lootedItems.Count})"
|
||||
: "Recently Looted";
|
||||
|
||||
_headerTextNode.String = _fullHeaderText;
|
||||
}
|
||||
|
||||
public void BeginHeaderHover()
|
||||
{
|
||||
_hoverRefs++;
|
||||
if (_hoverRefs != 1) return;
|
||||
|
||||
_headerExpanded = true;
|
||||
ApplyHeaderVisualStateAndSize();
|
||||
HeaderHoverChanged?.Invoke(this, true);
|
||||
}
|
||||
|
||||
public void EndHeaderHover()
|
||||
{
|
||||
if (_hoverRefs <= 0) return;
|
||||
|
||||
_hoverRefs--;
|
||||
if (_hoverRefs != 0) return;
|
||||
|
||||
_headerExpanded = false;
|
||||
ApplyHeaderVisualStateAndSize();
|
||||
HeaderHoverChanged?.Invoke(this, false);
|
||||
}
|
||||
|
||||
private void ApplyHeaderVisualStateAndSize()
|
||||
{
|
||||
var flags = _headerTextNode.TextFlags;
|
||||
flags &= ~(TextFlags.WordWrap | TextFlags.MultiLine);
|
||||
|
||||
if (_headerExpanded)
|
||||
{
|
||||
flags &= ~(TextFlags.OverflowHidden | TextFlags.Ellipsis);
|
||||
_headerTextNode.TextFlags = flags;
|
||||
|
||||
if (!string.IsNullOrEmpty(_fullHeaderText))
|
||||
_headerTextNode.String = _fullHeaderText;
|
||||
|
||||
Vector2 drawSize = _headerTextNode.GetTextDrawSize();
|
||||
float expandedWidth = MathF.Max(_baseHeaderWidth, drawSize.X + 4f);
|
||||
_headerTextNode.Size = _headerTextNode.Size with { X = expandedWidth };
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerTextNode.Size = _headerTextNode.Size with { X = _baseHeaderWidth };
|
||||
|
||||
if (!string.IsNullOrEmpty(_fullHeaderText))
|
||||
_headerTextNode.String = _fullHeaderText;
|
||||
|
||||
flags |= TextFlags.OverflowHidden | TextFlags.Ellipsis;
|
||||
_headerTextNode.TextFlags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncItemGrid()
|
||||
{
|
||||
_itemGridNode.SyncWithListData(
|
||||
_lootedItems,
|
||||
node => node.LootedItem,
|
||||
CreateLootedItemNode);
|
||||
}
|
||||
|
||||
private LootedItemDisplayNode CreateLootedItemNode(LootedItemInfo lootedItem)
|
||||
{
|
||||
var node = new LootedItemDisplayNode
|
||||
{
|
||||
OnDismiss = OnItemDismissed,
|
||||
};
|
||||
node.SetLootedItem(lootedItem);
|
||||
return node;
|
||||
}
|
||||
|
||||
private void OnItemDismissed(LootedItemDisplayNode node)
|
||||
{
|
||||
int index = node.LootedItem.Index;
|
||||
OnDismissItem?.Invoke(index);
|
||||
}
|
||||
|
||||
private void RecalculateSize()
|
||||
{
|
||||
int itemCount = _lootedItems.Count;
|
||||
|
||||
if (itemCount == 0)
|
||||
{
|
||||
float width = MinWidth;
|
||||
Size = new Vector2(width, HeaderHeight);
|
||||
_baseHeaderWidth = width - ClearButtonSize - 4;
|
||||
_headerTextNode.Size = new Vector2(_baseHeaderWidth, HeaderHeight);
|
||||
_clearButton.Position = new Vector2(width - ClearButtonSize, (HeaderHeight - ClearButtonSize) / 2);
|
||||
_clearButton.IsVisible = false;
|
||||
_itemGridNode.Position = new Vector2(0, HeaderHeight);
|
||||
_itemGridNode.Size = new Vector2(width, 0);
|
||||
ApplyHeaderVisualStateAndSize();
|
||||
return;
|
||||
}
|
||||
|
||||
int itemsPerLine = Math.Max(1, _itemGridNode.ItemsPerLine);
|
||||
int rows = (itemCount + itemsPerLine - 1) / itemsPerLine;
|
||||
int actualColumns = Math.Min(itemCount, itemsPerLine);
|
||||
|
||||
const float cellW = 42f;
|
||||
const float cellH = 46f;
|
||||
|
||||
float hPad = _itemGridNode.HorizontalPadding;
|
||||
float vPad = _itemGridNode.VerticalPadding;
|
||||
|
||||
float calculatedWidth = Math.Max(MinWidth, actualColumns * cellW + (actualColumns - 1) * hPad);
|
||||
float gridHeight = rows * cellH + (rows - 1) * vPad;
|
||||
float totalHeight = HeaderHeight + gridHeight;
|
||||
|
||||
Size = new Vector2(calculatedWidth, totalHeight);
|
||||
_baseHeaderWidth = calculatedWidth - ClearButtonSize - 4;
|
||||
_headerTextNode.Size = new Vector2(_baseHeaderWidth, HeaderHeight);
|
||||
_clearButton.Position = new Vector2(calculatedWidth - ClearButtonSize, (HeaderHeight - ClearButtonSize) / 2);
|
||||
_clearButton.IsVisible = true;
|
||||
_itemGridNode.Position = new Vector2(0, HeaderHeight);
|
||||
_itemGridNode.Size = new Vector2(calculatedWidth, gridHeight);
|
||||
ApplyHeaderVisualStateAndSize();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System. Numerics;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes.Inventory;
|
||||
|
||||
Reference in New Issue
Block a user