Files
AetherBags/KamiToolKit/Nodes/Layout/ListBoxNode.cs
T
KnackAtNite 8db4ce6094
Debug Build and Test / Build against Latest Dalamud (push) Has been cancelled
Debug Build and Test / Build against Staging Dalamud (push) Has been cancelled
Initial commit: AetherBags + KamiToolKit for FC Gitea
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 14:46:31 -05:00

201 lines
7.0 KiB
C#

using System;
using System.Linq;
using System.Numerics;
using KamiToolKit.Enums;
namespace KamiToolKit.Nodes;
/// Node that manages the layout of other nodes
public class ListBoxNode : LayoutListNode {
public readonly BackgroundImageNode Background;
public readonly BorderNineGridNode Border;
public ListBoxNode() {
Background = new BackgroundImageNode {
IsVisible = false,
};
Background.AttachNode(this);
Border = new BorderNineGridNode {
IsVisible = false,
};
Border.AttachNode(this);
}
public LayoutAnchor LayoutAnchor {
get;
set {
field = value;
RecalculateLayout();
}
}
public bool FitContents {
get;
set {
field = value;
RecalculateLayout();
Size = GetMinimumSize();
}
}
public LayoutOrientation LayoutOrientation {
get;
set {
field = value;
RecalculateLayout();
}
}
public Vector4 BackgroundColor {
get => Background.Color;
set => Background.Color = value;
}
public bool ShowBackground {
get => Background.IsVisible;
set => Background.IsVisible = value;
}
public bool ShowBorder {
get => Border.IsVisible;
set => Border.IsVisible = value;
}
public override float Height {
get => base.Height;
set => base.Height = FitContents ? GetMinimumSize().Y : value;
}
public override float Width {
get => base.Width;
set => base.Width = FitContents ? GetMinimumSize().X : value;
}
protected override void OnSizeChanged() {
base.OnSizeChanged();
Background.Size = Size;
Border.Size = Size + new Vector2(30.0f, 30.0f);
Border.Position = -new Vector2(15.0f, 15.0f);
RecalculateLayout();
}
protected override void OnRecalculateLayout() {
var runningPosition = LayoutOrientation switch {
LayoutOrientation.Vertical when LayoutAnchor is LayoutAnchor.TopLeft or LayoutAnchor.TopRight
=> GetLayoutStartPosition() + new Vector2(0.0f, FirstItemSpacing),
LayoutOrientation.Vertical when LayoutAnchor is LayoutAnchor.BottomLeft or LayoutAnchor.BottomRight
=> GetLayoutStartPosition() - new Vector2(0.0f, FirstItemSpacing),
LayoutOrientation.Horizontal when LayoutAnchor is LayoutAnchor.BottomLeft or LayoutAnchor.TopLeft
=> GetLayoutStartPosition() + new Vector2(FirstItemSpacing, 0.0f),
LayoutOrientation.Horizontal when LayoutAnchor is LayoutAnchor.BottomRight or LayoutAnchor.TopRight
=> GetLayoutStartPosition() - new Vector2(FirstItemSpacing, 0.0f),
_ => Vector2.Zero,
};
foreach (var node in NodeList.Where(node => node.IsVisible)) {
if (LayoutOrientation is LayoutOrientation.Vertical) {
switch (LayoutAnchor) {
case LayoutAnchor.TopLeft:
node.Position = runningPosition;
runningPosition.Y += node.Height * node.Scale.Y + ItemSpacing;
break;
case LayoutAnchor.TopRight:
node.Position = runningPosition - new Vector2(node.Width * node.Scale.X, 0.0f);
runningPosition.Y += node.Height * node.Scale.Y + ItemSpacing;
break;
case LayoutAnchor.BottomLeft:
node.Position = runningPosition - new Vector2(0.0f, node.Height * node.Scale.Y);
runningPosition.Y -= node.Height * node.Scale.Y + ItemSpacing;
break;
case LayoutAnchor.BottomRight:
node.Position = runningPosition - new Vector2(node.Width * node.Scale.X, node.Height * node.Scale.Y);
runningPosition.Y -= node.Height * node.Scale.Y + ItemSpacing;
break;
}
}
else if (LayoutOrientation is LayoutOrientation.Horizontal) {
switch (LayoutAnchor) {
case LayoutAnchor.TopLeft:
node.Position = runningPosition;
runningPosition.X += node.Width * node.Scale.X + ItemSpacing;
break;
case LayoutAnchor.TopRight:
node.Position = runningPosition - new Vector2(node.Width * node.Scale.X, 0.0f);
runningPosition.X -= node.Width * node.Scale.X + ItemSpacing;
break;
case LayoutAnchor.BottomLeft:
node.Position = runningPosition - new Vector2(0.0f, node.Height * node.Scale.Y);
runningPosition.X += node.Width * node.Scale.X + ItemSpacing;
break;
case LayoutAnchor.BottomRight:
node.Position = runningPosition - new Vector2(node.Width * node.Scale.X, node.Height * node.Scale.Y);
runningPosition.X -= node.Width * node.Scale.X + ItemSpacing;
break;
}
}
}
}
public override void AddNode(NodeBase? node) {
base.AddNode(node);
Size = GetMinimumSize();
}
public override void RemoveNode(NodeBase node) {
base.RemoveNode(node);
Size = GetMinimumSize();
}
/// <summary>
/// Get the current minimum size that would contain all the nodes including their margins.
/// </summary>
public Vector2 GetMinimumSize() {
var size = LayoutOrientation switch {
LayoutOrientation.Vertical => new Vector2(0.0f, FirstItemSpacing),
LayoutOrientation.Horizontal => new Vector2(FirstItemSpacing, 0.0f),
_ => Vector2.Zero,
};
foreach (var node in NodeList.Where(node => node.IsVisible)) {
switch (LayoutOrientation) {
// Horizontal we take max height, and add widths
case LayoutOrientation.Horizontal:
size.Y = MathF.Max(size.Y, node.Height);
size.X += node.Width + ItemSpacing;
break;
// Vertical we take max width, and add heights
case LayoutOrientation.Vertical:
size.X = MathF.Max(size.X, node.Width);
size.Y += node.Height + ItemSpacing;
break;
}
}
return size;
}
private Vector2 GetLayoutStartPosition() => LayoutAnchor switch {
LayoutAnchor.TopLeft => Vector2.Zero,
LayoutAnchor.TopRight => new Vector2(Width, 0.0f),
LayoutAnchor.BottomLeft => new Vector2(0.0f, Height),
LayoutAnchor.BottomRight => new Vector2(Width, Height),
_ => throw new ArgumentOutOfRangeException(),
};
}