WIP changes for layout engine and config
This commit is contained in:
@@ -7,6 +7,10 @@ public class GeneralSettings
|
|||||||
{
|
{
|
||||||
public InventoryStackMode StackMode { get; set; } = InventoryStackMode.AggregateByItemId;
|
public InventoryStackMode StackMode { get; set; } = InventoryStackMode.AggregateByItemId;
|
||||||
public bool DebugEnabled { get; set; } = false;
|
public bool DebugEnabled { get; set; } = false;
|
||||||
|
public bool CompactPackingEnabled { get; set; } = true;
|
||||||
|
public int CompactLookahead { get; set; } = 24;
|
||||||
|
public bool CompactPreferLargestFit { get; set; } = true;
|
||||||
|
public bool CompactStableInsert { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum InventoryStackMode : byte
|
public enum InventoryStackMode : byte
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using KamiToolKit.Nodes;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes.Configuration;
|
||||||
|
|
||||||
|
internal class ConfigurationRoot : TabbedVerticalListNode
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using AetherBags.Configuration;
|
||||||
|
using AetherBags.Nodes.Configuration.Layout;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using AetherBags.Configuration;
|
|
||||||
using KamiToolKit.Nodes;
|
|
||||||
|
|
||||||
namespace AetherBags.Nodes.Configuration;
|
namespace AetherBags.Nodes.Configuration;
|
||||||
|
|
||||||
@@ -10,7 +11,8 @@ public sealed class GeneralScrollingAreaNode : ScrollingAreaNode<VerticalListNod
|
|||||||
{
|
{
|
||||||
private readonly CheckboxNode _debugCheckboxNode = null!;
|
private readonly CheckboxNode _debugCheckboxNode = null!;
|
||||||
private readonly LabeledDropdownNode _stackDropDown = null!;
|
private readonly LabeledDropdownNode _stackDropDown = null!;
|
||||||
public GeneralScrollingAreaNode()
|
|
||||||
|
public unsafe GeneralScrollingAreaNode()
|
||||||
{
|
{
|
||||||
GeneralSettings config = System.Config.General;
|
GeneralSettings config = System.Config.General;
|
||||||
|
|
||||||
@@ -31,6 +33,8 @@ public sealed class GeneralScrollingAreaNode : ScrollingAreaNode<VerticalListNod
|
|||||||
};
|
};
|
||||||
ContentNode.AddNode(_stackDropDown);
|
ContentNode.AddNode(_stackDropDown);
|
||||||
|
|
||||||
|
ContentNode.AddNode(new LayoutConfigurationNode());
|
||||||
|
|
||||||
_debugCheckboxNode = new CheckboxNode
|
_debugCheckboxNode = new CheckboxNode
|
||||||
{
|
{
|
||||||
Size = new Vector2(300, 20),
|
Size = new Vector2(300, 20),
|
||||||
@@ -40,7 +44,6 @@ public sealed class GeneralScrollingAreaNode : ScrollingAreaNode<VerticalListNod
|
|||||||
OnClick = isChecked => { config.DebugEnabled = isChecked; }
|
OnClick = isChecked => { config.DebugEnabled = isChecked; }
|
||||||
};
|
};
|
||||||
ContentNode.AddNode(_debugCheckboxNode);
|
ContentNode.AddNode(_debugCheckboxNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshInventory() => System.AddonInventoryWindow.ManualRefresh();
|
private void RefreshInventory() => System.AddonInventoryWindow.ManualRefresh();
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using AetherBags.Configuration;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes.Configuration.Layout;
|
||||||
|
|
||||||
|
internal sealed class CompactLookaheadNode : SimpleComponentNode
|
||||||
|
{
|
||||||
|
public readonly NumericInputNode CompactLookahead = null!;
|
||||||
|
|
||||||
|
public unsafe CompactLookaheadNode()
|
||||||
|
{
|
||||||
|
GeneralSettings config = System.Config.General;
|
||||||
|
|
||||||
|
var titleNode = new LabelTextNode
|
||||||
|
{
|
||||||
|
Size = Size with { Y = 24 },
|
||||||
|
String = "Compact Lookahead",
|
||||||
|
};
|
||||||
|
titleNode.AttachNode(this);
|
||||||
|
|
||||||
|
CompactLookahead = new NumericInputNode
|
||||||
|
{
|
||||||
|
Position = Position with { X = 240 },
|
||||||
|
Size = Size with { X = 88 },
|
||||||
|
IsVisible = true,
|
||||||
|
Value = config.CompactLookahead,
|
||||||
|
OnValueUpdate = value =>
|
||||||
|
{
|
||||||
|
config.CompactLookahead = value;
|
||||||
|
System.AddonInventoryWindow.ManualRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CompactLookahead.ComponentBase->SetEnabledState(config.CompactPackingEnabled);
|
||||||
|
CompactLookahead.AttachNode(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using AetherBags.Configuration;
|
||||||
|
using KamiToolKit.Nodes;
|
||||||
|
using KamiToolKit.Classes;
|
||||||
|
|
||||||
|
namespace AetherBags.Nodes.Configuration.Layout;
|
||||||
|
|
||||||
|
internal class LayoutConfigurationNode : TabbedVerticalListNode
|
||||||
|
{
|
||||||
|
private readonly CompactLookaheadNode _compactLookaheadNode = null!;
|
||||||
|
private readonly CheckboxNode _preferLargestFitCheckboxNode = null!;
|
||||||
|
private readonly CheckboxNode _useStableInsertCheckboxNode = null!;
|
||||||
|
|
||||||
|
public unsafe LayoutConfigurationNode()
|
||||||
|
{
|
||||||
|
GeneralSettings config = System.Config.General;
|
||||||
|
|
||||||
|
var titleNode = new LabelTextNode
|
||||||
|
{
|
||||||
|
Size = Size with { Y = 18 },
|
||||||
|
String = "Layout Configuration",
|
||||||
|
TextColor = ColorHelper.GetColor(2),
|
||||||
|
TextOutlineColor = ColorHelper.GetColor(0),
|
||||||
|
};
|
||||||
|
AddNode(titleNode);
|
||||||
|
|
||||||
|
AddTab(1);
|
||||||
|
|
||||||
|
var compactPackingCheckboxNode = new CheckboxNode
|
||||||
|
{
|
||||||
|
Size = Size with { Y = 18 },
|
||||||
|
IsVisible = true,
|
||||||
|
String = "Use Compact Packing",
|
||||||
|
IsChecked = config.CompactPackingEnabled,
|
||||||
|
OnClick = isChecked =>
|
||||||
|
{
|
||||||
|
config.CompactPackingEnabled = isChecked;
|
||||||
|
_preferLargestFitCheckboxNode.IsEnabled = isChecked;
|
||||||
|
_useStableInsertCheckboxNode.IsEnabled = isChecked;
|
||||||
|
_compactLookaheadNode.CompactLookahead.ComponentBase->SetEnabledState(isChecked);
|
||||||
|
System.AddonInventoryWindow.ManualRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddNode(compactPackingCheckboxNode);
|
||||||
|
|
||||||
|
AddTab(1);
|
||||||
|
_preferLargestFitCheckboxNode = new CheckboxNode
|
||||||
|
{
|
||||||
|
Size = Size with { Y = 18 },
|
||||||
|
IsVisible = true,
|
||||||
|
String = "Prefer Largest Fit",
|
||||||
|
IsEnabled = config.CompactPackingEnabled,
|
||||||
|
IsChecked = config.CompactPreferLargestFit,
|
||||||
|
OnClick = isChecked =>
|
||||||
|
{
|
||||||
|
config.CompactPreferLargestFit = isChecked;
|
||||||
|
System.AddonInventoryWindow.ManualRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddNode(_preferLargestFitCheckboxNode);
|
||||||
|
|
||||||
|
_useStableInsertCheckboxNode = new CheckboxNode
|
||||||
|
{
|
||||||
|
Size = Size with { Y = 18 },
|
||||||
|
IsVisible = true,
|
||||||
|
String = "Use Stable Insert",
|
||||||
|
IsEnabled = config.CompactPackingEnabled,
|
||||||
|
IsChecked = config.CompactStableInsert,
|
||||||
|
OnClick = isChecked =>
|
||||||
|
{
|
||||||
|
config.CompactStableInsert = isChecked;
|
||||||
|
System.AddonInventoryWindow.ManualRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddNode(_useStableInsertCheckboxNode);
|
||||||
|
|
||||||
|
SubtractTab(1);
|
||||||
|
_compactLookaheadNode = new CompactLookaheadNode
|
||||||
|
{
|
||||||
|
Size = new Vector2(320, 20)
|
||||||
|
};
|
||||||
|
AddNode(_compactLookaheadNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,12 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
private float _lastVSpace = float.NaN;
|
private float _lastVSpace = float.NaN;
|
||||||
private float _lastTopPadding = float.NaN;
|
private float _lastTopPadding = float.NaN;
|
||||||
private float _lastBottomPadding = float.NaN;
|
private float _lastBottomPadding = float.NaN;
|
||||||
|
private bool _lastuseCompactPacking;
|
||||||
|
private bool _lastpreferLargestFit;
|
||||||
|
private bool _lastuseStableInsert;
|
||||||
|
private int _lastCompactLookahead;
|
||||||
|
|
||||||
|
private int[] _orderScratch = Array.Empty<int>();
|
||||||
|
|
||||||
public WrappingGridNode()
|
public WrappingGridNode()
|
||||||
{
|
{
|
||||||
@@ -55,6 +61,22 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (System.Config.General.CompactPackingEnabled)
|
||||||
|
{
|
||||||
|
if (_rows.Count != 0 && LayoutParamsMatchLast() && NodeSetMatchesExistingLayout(count))
|
||||||
|
{
|
||||||
|
RepositionExistingRows();
|
||||||
|
_requiredHeightDirty = true;
|
||||||
|
RememberLayoutParams();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FullReflowCompact(count);
|
||||||
|
_requiredHeightDirty = true;
|
||||||
|
RememberLayoutParams();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_rows.Count != 0 && TryUpdateLayoutWithoutReflowOrTailReflow(count))
|
if (_rows.Count != 0 && TryUpdateLayoutWithoutReflowOrTailReflow(count))
|
||||||
{
|
{
|
||||||
_requiredHeightDirty = true;
|
_requiredHeightDirty = true;
|
||||||
@@ -67,6 +89,20 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
RememberLayoutParams();
|
RememberLayoutParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool NodeSetMatchesExistingLayout(int count)
|
||||||
|
{
|
||||||
|
if (_rowIndex.Count != count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!_rowIndex.ContainsKey(NodeList[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool TryUpdateLayoutWithoutReflowOrTailReflow(int count)
|
private bool TryUpdateLayoutWithoutReflowOrTailReflow(int count)
|
||||||
{
|
{
|
||||||
if (!LayoutParamsMatchLast())
|
if (!LayoutParamsMatchLast())
|
||||||
@@ -347,6 +383,134 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void FullReflowCompact(int count)
|
||||||
|
{
|
||||||
|
RecycleAllRows();
|
||||||
|
_rowIndex.Clear();
|
||||||
|
_rowIndex.EnsureCapacity(count);
|
||||||
|
|
||||||
|
float availableWidth = Width;
|
||||||
|
float hSpace = HorizontalSpacing;
|
||||||
|
float vSpace = VerticalSpacing;
|
||||||
|
float startX = FirstItemSpacing;
|
||||||
|
|
||||||
|
float y = TopPadding;
|
||||||
|
|
||||||
|
EnsureOrderScratch(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
_orderScratch[i] = i;
|
||||||
|
|
||||||
|
int lookahead = System.Config.General.CompactLookahead;
|
||||||
|
if (lookahead < 0) lookahead = 0;
|
||||||
|
|
||||||
|
int p = 0;
|
||||||
|
int rowIdx = 0;
|
||||||
|
|
||||||
|
while (p < count)
|
||||||
|
{
|
||||||
|
List<NodeBase> row = RentRowList(capacityHint: 8);
|
||||||
|
|
||||||
|
float x = startX;
|
||||||
|
float rowHeight = 0f;
|
||||||
|
|
||||||
|
while (p < count)
|
||||||
|
{
|
||||||
|
int idx = _orderScratch[p];
|
||||||
|
NodeBase node = NodeList[idx];
|
||||||
|
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 >= count) end = count - 1;
|
||||||
|
|
||||||
|
for (int s = p + 1; s <= end; s++)
|
||||||
|
{
|
||||||
|
int candIdx = _orderScratch[s];
|
||||||
|
NodeBase cand = NodeList[candIdx];
|
||||||
|
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 idx = _orderScratch[p];
|
||||||
|
NodeBase node = NodeList[idx];
|
||||||
|
float w = node.Width;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public float GetRequiredHeight()
|
public float GetRequiredHeight()
|
||||||
{
|
{
|
||||||
@@ -400,15 +564,29 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
_rowPool.Push(row);
|
_rowPool.Push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool NearlyEqual(float a, float b)
|
||||||
|
{
|
||||||
|
float diff = MathF.Abs(a - b);
|
||||||
|
if (diff <= 0.05f) return true;
|
||||||
|
|
||||||
|
float max = MathF.Max(MathF.Abs(a), MathF.Abs(b));
|
||||||
|
return diff <= max * 0.0005f;
|
||||||
|
}
|
||||||
|
|
||||||
private bool LayoutParamsMatchLast()
|
private bool LayoutParamsMatchLast()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
_lastAvailableWidth == Width &&
|
NearlyEqual(_lastAvailableWidth, Width) &&
|
||||||
_lastStartX == FirstItemSpacing &&
|
NearlyEqual(_lastStartX, FirstItemSpacing) &&
|
||||||
_lastHSpace == HorizontalSpacing &&
|
NearlyEqual(_lastHSpace, HorizontalSpacing) &&
|
||||||
_lastVSpace == VerticalSpacing &&
|
NearlyEqual(_lastVSpace, VerticalSpacing) &&
|
||||||
_lastTopPadding == TopPadding &&
|
NearlyEqual(_lastTopPadding, TopPadding) &&
|
||||||
_lastBottomPadding == BottomPadding;
|
NearlyEqual(_lastBottomPadding, BottomPadding) &&
|
||||||
|
_lastuseCompactPacking == System.Config.General.CompactPackingEnabled &&
|
||||||
|
_lastpreferLargestFit == System.Config.General.CompactPreferLargestFit &&
|
||||||
|
_lastuseStableInsert == System.Config.General.CompactStableInsert &&
|
||||||
|
_lastCompactLookahead == System.Config.General.CompactLookahead;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RememberLayoutParams()
|
private void RememberLayoutParams()
|
||||||
@@ -419,6 +597,22 @@ public sealed class WrappingGridNode<T> : LayoutListNode where T : NodeBase
|
|||||||
_lastVSpace = VerticalSpacing;
|
_lastVSpace = VerticalSpacing;
|
||||||
_lastTopPadding = TopPadding;
|
_lastTopPadding = TopPadding;
|
||||||
_lastBottomPadding = BottomPadding;
|
_lastBottomPadding = BottomPadding;
|
||||||
|
|
||||||
|
_lastuseCompactPacking = System.Config.General.CompactPackingEnabled;
|
||||||
|
_lastpreferLargestFit = System.Config.General.CompactPreferLargestFit;
|
||||||
|
_lastuseStableInsert = System.Config.General.CompactStableInsert;
|
||||||
|
_lastCompactLookahead = System.Config.General.CompactLookahead;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureOrderScratch(int needed)
|
||||||
|
{
|
||||||
|
if (_orderScratch.Length >= needed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int newSize = _orderScratch.Length == 0 ? 64 : _orderScratch.Length;
|
||||||
|
while (newSize < needed) newSize *= 2;
|
||||||
|
|
||||||
|
_orderScratch = new int[newSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class RowsReadOnlyView : IReadOnlyList<IReadOnlyList<NodeBase>>
|
private sealed class RowsReadOnlyView : IReadOnlyList<IReadOnlyList<NodeBase>>
|
||||||
|
|||||||
Reference in New Issue
Block a user