Improve layout
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAgentSatisfactionSupply_002Ecs_002Fl_003AC_0021_003FUsers_003FJeffro_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb2cd0663609440e590f52980cafc1ba3822648_003F28_003Ffa48b62e_003FAgentSatisfactionSupply_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAgentSatisfactionSupply_002Ecs_002Fl_003AC_0021_003FUsers_003FJeffro_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb2cd0663609440e590f52980cafc1ba3822648_003F28_003Ffa48b62e_003FAgentSatisfactionSupply_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AItem_002Eg_002Ecs_002Fl_003AC_0021_003FUsers_003FJeffro_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8ec7cc8a18dbb6a6f3c21f8adcb4e2661dc7979_003FItem_002Eg_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using AetherBags.Extensions;
|
||||
@@ -15,58 +17,33 @@ namespace AetherBags.Addons;
|
||||
|
||||
public class AddonInventoryWindow : NativeAddon
|
||||
{
|
||||
private InventoryCategoryNode _categoryNode;
|
||||
private InventoryDragDropNode _dragDropNode;
|
||||
private WrappingGridNode<InventoryCategoryNode> _categoriesNode;
|
||||
|
||||
// Window constraints
|
||||
private const float MinWindowWidth = 300;
|
||||
private const float MaxWindowWidth = 800;
|
||||
private const float MinWindowHeight = 200;
|
||||
private const float MaxWindowHeight = 1000;
|
||||
|
||||
// Layout settings
|
||||
private const float CategorySpacing = 10;
|
||||
private const float ItemSize = 40;
|
||||
private const float ItemPadding = 6;
|
||||
|
||||
protected override unsafe void OnSetup(AtkUnitBase* addon)
|
||||
{
|
||||
Services.AddonLifecycle.RegisterListener(AddonEvent.PostRequestedUpdate, "Inventory", OnInventoryUpdate);
|
||||
_categoryNode = new InventoryCategoryNode
|
||||
addon->SubscribeAtkArrayData(1, (int)NumberArrayType.Inventory);
|
||||
_categoriesNode = new WrappingGridNode<InventoryCategoryNode>
|
||||
{
|
||||
Position = ContentStartPosition,
|
||||
Size = ContentSize,
|
||||
Category = new CategoryInfo
|
||||
{
|
||||
Name = "AetherBags",
|
||||
},
|
||||
Items = InventoryState.GetInventoryItems()
|
||||
HorizontalSpacing = CategorySpacing,
|
||||
VerticalSpacing = CategorySpacing
|
||||
};
|
||||
_categoryNode.AttachNode(this);
|
||||
/*
|
||||
var data = InventoryState.GetInventoryItems().Find(item => item.Name.Contains("Cookie"));
|
||||
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var item = data.Item;
|
||||
_dragDropNode = new InventoryDragDropNode
|
||||
{
|
||||
Size = new Vector2(48),
|
||||
IsVisible = true,
|
||||
IconId = data.IconId,
|
||||
AcceptedType = DragDropType.Nothing,
|
||||
IsDraggable = false,
|
||||
Payload = new DragDropPayload
|
||||
{
|
||||
Type = DragDropType.Item,
|
||||
Int1 = (int)data.Item.Container,
|
||||
Int2 = (int)data.Item.ItemId,
|
||||
},
|
||||
IsClickable = true,
|
||||
OnRollOver = node => node.ShowInventoryItemTooltip(data.Item.Container, data.Item.Slot),
|
||||
OnRollOut = node => node.HideTooltip(),
|
||||
OnClicked = _ =>
|
||||
{
|
||||
|
||||
AgentInventoryContext* context = AgentInventoryContext.Instance();
|
||||
context->OpenForItemSlot(data.Item.Container, data.Item.Slot, 0, context->AddonId);
|
||||
//item.UseItem();
|
||||
},
|
||||
ItemInfo = data
|
||||
};
|
||||
_dragDropNode.AttachNode(this);
|
||||
}
|
||||
*/
|
||||
_categoriesNode.AttachNode(this);
|
||||
|
||||
RefreshCategories();
|
||||
}
|
||||
|
||||
protected override unsafe void OnUpdate(AtkUnitBase* addon)
|
||||
@@ -76,11 +53,94 @@ public class AddonInventoryWindow : NativeAddon
|
||||
|
||||
private void OnInventoryUpdate(AddonEvent type, AddonArgs args)
|
||||
{
|
||||
RefreshCategories();
|
||||
}
|
||||
|
||||
protected override unsafe void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) {
|
||||
RefreshCategories();
|
||||
}
|
||||
|
||||
private void RefreshCategories()
|
||||
{
|
||||
var categories = InventoryState.GetInventoryItemCategories();
|
||||
|
||||
float maxContentWidth = MaxWindowWidth - (ContentStartPosition.X * 2);
|
||||
int maxItemsPerLine = CalculateOptimalItemsPerLine(maxContentWidth);
|
||||
|
||||
_categoriesNode.SyncWithListData(
|
||||
categories,
|
||||
node => node.CategorizedInventory,
|
||||
data =>
|
||||
{
|
||||
var node = new InventoryCategoryNode
|
||||
{
|
||||
Size = ContentSize with { Y = 120 },
|
||||
CategorizedInventory = data
|
||||
};
|
||||
|
||||
UpdateItemsPerLine(node, maxItemsPerLine);
|
||||
return node;
|
||||
});
|
||||
|
||||
foreach (InventoryCategoryNode node in _categoriesNode.GetNodes<InventoryCategoryNode>())
|
||||
{
|
||||
UpdateItemsPerLine(node, maxItemsPerLine);
|
||||
}
|
||||
|
||||
AutoSizeWindow();
|
||||
}
|
||||
|
||||
private static void UpdateItemsPerLine(InventoryCategoryNode node, int maxItemsPerLine)
|
||||
{
|
||||
int itemCount = node.CategorizedInventory.Items.Count;
|
||||
int itemsPerLine = Math.Min(itemCount, maxItemsPerLine);
|
||||
node.SetItemsPerLine(itemsPerLine);
|
||||
}
|
||||
|
||||
private int CalculateOptimalItemsPerLine(float availableWidth)
|
||||
{
|
||||
float itemWithPadding = ItemSize + ItemPadding;
|
||||
int maxItems = (int)Math.Floor((availableWidth + ItemPadding) / itemWithPadding);
|
||||
|
||||
return Math.Clamp(maxItems, 1, 15);
|
||||
}
|
||||
|
||||
private void AutoSizeWindow()
|
||||
{
|
||||
List<InventoryCategoryNode> childNodes = _categoriesNode.GetNodes<InventoryCategoryNode>().ToList();
|
||||
if (childNodes.Count == 0)
|
||||
{
|
||||
ResizeWindow(MinWindowWidth, MinWindowHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
float requiredWidth = childNodes.Max(node => node. Width);
|
||||
requiredWidth += ContentStartPosition.X * 2;
|
||||
float finalWidth = Math.Clamp(requiredWidth, MinWindowWidth, MaxWindowWidth);
|
||||
|
||||
float contentWidth = finalWidth - (ContentStartPosition.X * 2);
|
||||
_categoriesNode.Size = new Vector2(contentWidth, MaxWindowHeight);
|
||||
|
||||
_categoriesNode.RecalculateLayout();
|
||||
|
||||
float requiredHeight = _categoriesNode.GetRequiredHeight();
|
||||
requiredHeight += ContentStartPosition.Y + ContentStartPosition.X;
|
||||
|
||||
float finalHeight = Math.Clamp(requiredHeight, MinWindowHeight, MaxWindowHeight);
|
||||
|
||||
ResizeWindow(finalWidth, finalHeight);
|
||||
}
|
||||
|
||||
private void ResizeWindow(float width, float height)
|
||||
{
|
||||
SetWindowSize(width, height);
|
||||
_categoriesNode.Size = ContentSize;
|
||||
_categoriesNode.RecalculateLayout();
|
||||
}
|
||||
|
||||
protected override unsafe void OnFinalize(AtkUnitBase* addon)
|
||||
{
|
||||
Services.AddonLifecycle.UnregisterListener(OnInventoryUpdate);
|
||||
addon->UnsubscribeAtkArrayData(1, (int)NumberArrayType.Inventory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AetherBags;
|
||||
using Dalamud.Game.Addon.Lifecycle;
|
||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
public static class AddonLifecycleExtensions {
|
||||
extension(IAddonLifecycle addonLifecycle) {
|
||||
public void LogAddon(string addonName, params AddonEvent[] loggedModules) {
|
||||
if (loggedModules.Length is 0) {
|
||||
loggedModules = [
|
||||
AddonEvent.PostSetup,
|
||||
AddonEvent.PostOpen,
|
||||
AddonEvent.PostClose,
|
||||
AddonEvent.PostShow,
|
||||
AddonEvent.PostHide,
|
||||
AddonEvent.PostRefresh,
|
||||
AddonEvent.PostRequestedUpdate,
|
||||
AddonEvent.PreFinalize,
|
||||
];
|
||||
}
|
||||
|
||||
ActiveLoggers.TryAdd(addonName, loggedModules.ToList());
|
||||
foreach (var loggedModule in loggedModules) {
|
||||
addonLifecycle.RegisterListener(loggedModule, addonName, Logger);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnLogAddon(string addonName) {
|
||||
if (!ActiveLoggers.TryGetValue(addonName, out var loggedModules)) return;
|
||||
|
||||
foreach (var loggedModule in loggedModules) {
|
||||
addonLifecycle.UnregisterListener(loggedModule, addonName, Logger);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, List<AddonEvent>> ActiveLoggers = [];
|
||||
|
||||
private static void Logger(AddonEvent type, AddonArgs args) {
|
||||
switch (args) {
|
||||
case AddonReceiveEventArgs receiveEventArgs:
|
||||
Services.Logger.Debug($"[{args.AddonName}] {(AtkEventType)receiveEventArgs.AtkEventType}: {receiveEventArgs.EventParam}");
|
||||
break;
|
||||
|
||||
default:
|
||||
Services.Logger.Debug($"{args.AddonName} called {type.ToString().Replace("Post", string.Empty)}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AetherBags.Inventory;
|
||||
|
||||
public readonly record struct CategorizedInventory(CategoryInfo Category, List<ItemInfo> Items);
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using Dalamud.Game.Inventory;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace AetherBags.Inventory;
|
||||
|
||||
@@ -34,6 +35,22 @@ public static unsafe class InventoryState
|
||||
public static bool Contains(this List<InventoryType> inventoryTypes, GameInventoryType type)
|
||||
=> inventoryTypes.Contains((InventoryType)type);
|
||||
|
||||
public static List<CategorizedInventory> GetInventoryItemCategories()
|
||||
{
|
||||
var items = GetInventoryItems();
|
||||
|
||||
return items
|
||||
.GroupBy(GetItemUiCategoryKey)
|
||||
.OrderBy(g => g.Key)
|
||||
.Select(g =>
|
||||
{
|
||||
var category = GetCategoryInfoForKey(g.Key, g.FirstOrDefault());
|
||||
var list = g.OrderByDescending(i => i.ItemCount).ToList();
|
||||
return new CategorizedInventory(category, list);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<ItemInfo> GetInventoryItems() {
|
||||
List<InventoryType> inventories = [ InventoryType.Inventory1, InventoryType.Inventory2, InventoryType.Inventory3, InventoryType.Inventory4 ];
|
||||
List<InventoryItem> items = [];
|
||||
@@ -60,4 +77,35 @@ public static unsafe class InventoryState
|
||||
|
||||
return itemInfos;
|
||||
}
|
||||
|
||||
public static List<ItemInfo> GetInventoryItems(string filterString, bool invert = false)
|
||||
=> GetInventoryItems().Where(item => item.IsRegexMatch(filterString) != invert).ToList();
|
||||
|
||||
private static uint GetItemUiCategoryKey(ItemInfo info)
|
||||
=> info.UiCategory.RowId;
|
||||
|
||||
private static CategoryInfo GetCategoryInfoForKey(uint key, ItemInfo? sample)
|
||||
{
|
||||
if (key == 0)
|
||||
{
|
||||
return new CategoryInfo
|
||||
{
|
||||
Name = "Misc",
|
||||
Description = "Uncategorized items",
|
||||
};
|
||||
}
|
||||
|
||||
var uiCat = sample?.UiCategory.Value;
|
||||
var name = uiCat?.Name.ToString();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = $"Category\\ {key}";
|
||||
|
||||
return new CategoryInfo
|
||||
{
|
||||
Name = name,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using AetherBags.Extensions;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
|
||||
namespace AetherBags.Inventory;
|
||||
@@ -25,7 +26,7 @@ public class ItemInfo : IEquatable<ItemInfo> {
|
||||
|
||||
public int Rarity => ItemData.Rarity;
|
||||
|
||||
public int UiCategory => (int) ItemData.ItemUICategory.RowId;
|
||||
public RowRef<ItemUICategory> UiCategory => ItemData.ItemUICategory;
|
||||
|
||||
private string Description => ItemData.Description.ToString();
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using KamiToolKit;
|
||||
using KamiToolKit.Nodes;
|
||||
|
||||
namespace AetherBags.Nodes
|
||||
{
|
||||
public class HybridDirectionalStackNode<T> : LayoutListNode where T : NodeBase
|
||||
{
|
||||
public FlexGrowDirection GrowDirection
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
RecalculateLayout();
|
||||
}
|
||||
} = FlexGrowDirection.DownRight;
|
||||
|
||||
public bool Vertical
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
RecalculateLayout();
|
||||
}
|
||||
} = true;
|
||||
|
||||
public float Spacing
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
RecalculateLayout();
|
||||
}
|
||||
} = 1f;
|
||||
|
||||
public bool StretchCrossAxis
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
RecalculateLayout();
|
||||
}
|
||||
} = true;
|
||||
|
||||
protected override void InternalRecalculateLayout()
|
||||
{
|
||||
if (NodeList.Count == 0)
|
||||
return;
|
||||
|
||||
bool alignRight = GrowDirection is FlexGrowDirection.DownLeft or FlexGrowDirection.UpLeft;
|
||||
bool alignBottom = GrowDirection is FlexGrowDirection.UpRight or FlexGrowDirection.UpLeft;
|
||||
|
||||
float startX = alignRight ? Width : 0f;
|
||||
float startY = alignBottom ? Height : 0f;
|
||||
|
||||
float cursor = 0f;
|
||||
|
||||
for (int i = 0; i < NodeList.Count; i++)
|
||||
{
|
||||
var node = NodeList[i];
|
||||
|
||||
if (StretchCrossAxis)
|
||||
{
|
||||
if (Vertical)
|
||||
node.Width = Width;
|
||||
else
|
||||
node.Height = Height;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
if (Vertical)
|
||||
{
|
||||
x = alignRight ? startX - node.Width : startX;
|
||||
y = alignBottom ? startY - node.Height - cursor : startY + cursor;
|
||||
cursor += node.Height + Spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = alignRight ? startX - node.Width - cursor : startX + cursor;
|
||||
y = alignBottom ? startY - node.Height : startY;
|
||||
cursor += node.Width + Spacing;
|
||||
}
|
||||
|
||||
node.X = x;
|
||||
node.Y = y;
|
||||
AdjustNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AetherBags.Extensions;
|
||||
using AetherBags.Inventory;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using KamiToolKit.Classes;
|
||||
@@ -14,6 +15,15 @@ public class InventoryCategoryNode : SimpleComponentNode
|
||||
{
|
||||
private readonly TextNode _categoryNameTextNode;
|
||||
private readonly HybridDirectionalFlexNode<DragDropNode> _itemGridNode;
|
||||
|
||||
private const float ItemSize = 40;
|
||||
private const float ItemHorizontalPadding = 6;
|
||||
private const float ItemVerticalPadding = 6;
|
||||
private const float HeaderHeight = 16;
|
||||
private const float MinWidth = 40;
|
||||
|
||||
private float? _fixedWidth = null;
|
||||
|
||||
public InventoryCategoryNode()
|
||||
{
|
||||
_categoryNameTextNode = new TextNode
|
||||
@@ -25,63 +35,101 @@ public class InventoryCategoryNode : SimpleComponentNode
|
||||
|
||||
_itemGridNode = new HybridDirectionalFlexNode<DragDropNode>
|
||||
{
|
||||
Position = new Vector2(0, 16),
|
||||
Position = new Vector2(0, HeaderHeight),
|
||||
Size = new Vector2(240, 100),
|
||||
FillRowsFirst = true,
|
||||
ItemsPerLine = 10,
|
||||
HorizontalPadding = 6,
|
||||
VerticalPadding = 6,
|
||||
HorizontalPadding = ItemHorizontalPadding,
|
||||
VerticalPadding = ItemVerticalPadding,
|
||||
};
|
||||
_itemGridNode.AttachNode(this);
|
||||
}
|
||||
|
||||
public required CategoryInfo Category
|
||||
public required CategorizedInventory CategorizedInventory
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
|
||||
_categoryNameTextNode.String = value.Name;
|
||||
_categoryNameTextNode.TextColor = value.Color;
|
||||
_categoryNameTextNode.TooltipString = value.Description;
|
||||
_categoryNameTextNode.String = value.Category.Name;
|
||||
_categoryNameTextNode.TextColor = value.Category.Color;
|
||||
_categoryNameTextNode.TooltipString = value.Category.Description;
|
||||
|
||||
UpdateItemGrid();
|
||||
RecalculateSize();
|
||||
}
|
||||
}
|
||||
|
||||
public required List<ItemInfo> Items
|
||||
public void SetItemsPerLine(int itemsPerLine)
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
_itemGridNode.ItemsPerLine = itemsPerLine;
|
||||
RecalculateSize();
|
||||
}
|
||||
|
||||
UpdateItemGrid();
|
||||
public void SetFixedWidth(float width)
|
||||
{
|
||||
_fixedWidth = width;
|
||||
RecalculateSize();
|
||||
}
|
||||
|
||||
private void RecalculateSize()
|
||||
{
|
||||
int itemCount = CategorizedInventory.Items.Count;
|
||||
if (itemCount == 0)
|
||||
{
|
||||
float width = _fixedWidth ?? MinWidth;
|
||||
Size = new Vector2(width, HeaderHeight);
|
||||
_categoryNameTextNode.Size = _categoryNameTextNode.Size with { X = width };
|
||||
return;
|
||||
}
|
||||
|
||||
int itemsPerLine = Math.Max(1, _itemGridNode.ItemsPerLine);
|
||||
int rows = (int)Math.Ceiling((float)itemCount / itemsPerLine);
|
||||
|
||||
float calculatedWidth;
|
||||
if (_fixedWidth. HasValue)
|
||||
{
|
||||
calculatedWidth = _fixedWidth.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
int actualColumns = Math.Min(itemCount, itemsPerLine);
|
||||
calculatedWidth = actualColumns * ItemSize + (actualColumns - 1) * ItemHorizontalPadding;
|
||||
calculatedWidth = Math.Max(calculatedWidth, MinWidth);
|
||||
}
|
||||
|
||||
float height = HeaderHeight + rows * ItemSize + (rows - 1) * ItemVerticalPadding;
|
||||
|
||||
Size = new Vector2(calculatedWidth, height);
|
||||
_itemGridNode.Size = new Vector2(calculatedWidth, height - HeaderHeight);
|
||||
_categoryNameTextNode.Size = _categoryNameTextNode.Size with { X = calculatedWidth };
|
||||
}
|
||||
|
||||
private bool UpdateItemGrid()
|
||||
{
|
||||
var listUpdated = _itemGridNode.SyncWithListData(Items, node => node.ItemInfo, data => CreateInventoryDragDropNode(data));
|
||||
var listUpdated = _itemGridNode.SyncWithListData(CategorizedInventory.Items, node => node.ItemInfo, CreateInventoryDragDropNode);
|
||||
return listUpdated;
|
||||
}
|
||||
|
||||
private unsafe InventoryDragDropNode CreateInventoryDragDropNode(ItemInfo data)
|
||||
private InventoryDragDropNode CreateInventoryDragDropNode(ItemInfo data)
|
||||
{
|
||||
InventoryItem item = data.Item;
|
||||
InventoryDragDropNode node = new InventoryDragDropNode
|
||||
{
|
||||
Size = new Vector2(40),
|
||||
IsVisible = true,
|
||||
IconId = data.IconId,
|
||||
IconId = item.IconId,
|
||||
AcceptedType = DragDropType.Nothing,
|
||||
IsDraggable = false,
|
||||
Payload = new DragDropPayload
|
||||
{
|
||||
Type = DragDropType.Item,
|
||||
Int1 = (int)data.Item.Container,
|
||||
Int2 = (int)data.Item.ItemId,
|
||||
Int1 = (int)item.Container,
|
||||
Int2 = (int)item.ItemId,
|
||||
},
|
||||
IsClickable = true,
|
||||
OnRollOver = node => node.ShowInventoryItemTooltip(data.Item.Container, data.Item.Slot),
|
||||
OnRollOver = node => node.ShowInventoryItemTooltip(item.Container, item.Slot),
|
||||
OnRollOut = node => node.HideTooltip(),
|
||||
ItemInfo = data
|
||||
};
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using KamiToolKit;
|
||||
using KamiToolKit. Nodes;
|
||||
|
||||
namespace AetherBags.Nodes
|
||||
{
|
||||
public class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
||||
{
|
||||
public float HorizontalSpacing { get; set; } = 10;
|
||||
public float VerticalSpacing { get; set; } = 10;
|
||||
|
||||
private List<List<NodeBase>> _rows = new();
|
||||
|
||||
protected override void InternalRecalculateLayout()
|
||||
{
|
||||
if (NodeList. Count == 0)
|
||||
return;
|
||||
|
||||
_rows.Clear();
|
||||
|
||||
float availableWidth = Width;
|
||||
float currentX = 0f;
|
||||
float currentY = 0f;
|
||||
float rowHeight = 0f;
|
||||
List<NodeBase> currentRow = new();
|
||||
|
||||
foreach (var node in NodeList)
|
||||
{
|
||||
float nodeWidth = node.Width;
|
||||
float nodeHeight = node.Height;
|
||||
|
||||
if (currentX + nodeWidth > availableWidth && currentRow.Count > 0)
|
||||
{
|
||||
_rows.Add(currentRow);
|
||||
currentRow = new();
|
||||
currentY += rowHeight + VerticalSpacing;
|
||||
currentX = 0f;
|
||||
rowHeight = 0f;
|
||||
}
|
||||
|
||||
node.X = currentX;
|
||||
node. Y = currentY;
|
||||
AdjustNode(node);
|
||||
|
||||
currentX += nodeWidth + HorizontalSpacing;
|
||||
rowHeight = Math.Max(rowHeight, nodeHeight);
|
||||
currentRow.Add(node);
|
||||
}
|
||||
|
||||
if (currentRow.Count > 0)
|
||||
{
|
||||
_rows.Add(currentRow);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetRequiredHeight()
|
||||
{
|
||||
if (NodeList.Count == 0)
|
||||
return 0f;
|
||||
|
||||
float maxY = 0f;
|
||||
foreach (var node in NodeList)
|
||||
{
|
||||
float nodeBottom = node.Y + node.Height;
|
||||
maxY = Math. Max(maxY, nodeBottom);
|
||||
}
|
||||
|
||||
return maxY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ public class Plugin : IDalamudPlugin
|
||||
if (Services.ClientState.IsLoggedIn) {
|
||||
Services.Framework.RunOnFrameworkThread(OnLogin);
|
||||
}
|
||||
Services.AddonLifecycle.LogAddon("Inventory");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
Reference in New Issue
Block a user