Files
HSUI/Helpers/LayoutHelper.cs

323 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Numerics;
namespace HSUI.Helpers
{
public struct LayoutInfo
{
public readonly uint TotalRowCount;
public readonly uint TotalColCount;
public readonly uint RealRowCount;
public readonly uint RealColCount;
public readonly Vector2 ContentSize;
public LayoutInfo(uint totalRowCount, uint totalColCount, uint realRowCount, uint realColCount, Vector2 contentSize)
{
TotalRowCount = totalRowCount;
TotalColCount = totalColCount;
RealRowCount = realRowCount;
RealColCount = realColCount;
ContentSize = contentSize;
}
}
public static class LayoutHelper
{
// Calculates rows and columns. Used for status effect lists and party frames.
public static LayoutInfo CalculateLayout(
Vector2 maxSize,
Vector2 itemSize,
uint count,
Vector2 padding,
bool fillRowsFirst
)
{
uint rowCount = 1;
uint colCount = 1;
uint realRowCount = 1;
uint realColCount = 1;
if (maxSize.X < itemSize.X)
{
colCount = count;
realColCount = count;
}
else if (maxSize.Y < itemSize.Y)
{
rowCount = count;
realRowCount = count;
}
else
{
if (fillRowsFirst)
{
float remainingWidth = maxSize.X;
colCount = 0;
while (remainingWidth > 0)
{
remainingWidth -= (itemSize.X + padding.X);
colCount++;
}
if (itemSize.X * colCount + padding.X * (colCount - 1) > maxSize.X)
{
colCount = Math.Max(1, colCount - 1);
}
rowCount = (uint)Math.Ceiling((double)count / colCount);
int remaining = (int)(count - colCount);
while (remaining > 0)
{
realRowCount++;
remaining -= (int)colCount;
}
realColCount = Math.Min(count, colCount);
}
else
{
float remainingHeight = maxSize.Y;
rowCount = 0;
while (remainingHeight > 0)
{
remainingHeight -= (itemSize.Y + padding.Y);
rowCount++;
}
if (itemSize.Y * rowCount + padding.Y * (rowCount - 1) > maxSize.Y)
{
rowCount = Math.Max(1, rowCount - 1);
}
colCount = (uint)Math.Ceiling((double)count / rowCount);
int remaining = (int)(count - rowCount);
while (remaining > 0)
{
realColCount++;
remaining -= (int)rowCount;
}
realRowCount = Math.Min(count, rowCount);
}
}
Vector2 contentSize = new Vector2(
realColCount * itemSize.X + (realColCount - 1) * padding.X,
realRowCount * itemSize.Y + (realRowCount - 1) * padding.Y
);
return new LayoutInfo(rowCount, colCount, realRowCount, realColCount, contentSize);
}
private static List<GrowthDirections> DirectionOptionsValues = new List<GrowthDirections>()
{
GrowthDirections.Right | GrowthDirections.Down,
GrowthDirections.Right | GrowthDirections.Up,
GrowthDirections.Left | GrowthDirections.Down,
GrowthDirections.Left | GrowthDirections.Up,
GrowthDirections.Centered | GrowthDirections.Up,
GrowthDirections.Centered | GrowthDirections.Down,
GrowthDirections.Centered | GrowthDirections.Left,
GrowthDirections.Centered | GrowthDirections.Right
};
public static GrowthDirections GrowthDirectionsFromIndex(int index)
{
if (index > 0 && index < DirectionOptionsValues.Count)
{
return DirectionOptionsValues[index];
}
return DirectionOptionsValues[0];
}
public static int IndexFromGrowthDirections(GrowthDirections directions)
{
int index = DirectionOptionsValues.FindIndex(d => d == directions);
return index > 0 ? index : 0;
}
public static bool GetFillsRowsFirst(bool fallback, GrowthDirections directions)
{
if ((directions & GrowthDirections.Centered) != 0)
{
if ((directions & GrowthDirections.Up) != 0 || (directions & GrowthDirections.Down) != 0)
{
return true;
}
else if ((directions & GrowthDirections.Left) != 0 || (directions & GrowthDirections.Right) != 0)
{
return false;
}
}
return fallback;
}
public static void CalculateAxisDirections(
GrowthDirections growthDirections,
int row,
int col,
uint elementCount,
Vector2 size,
Vector2 iconSize,
Vector2 iconPadding,
out Vector2 direction,
out Vector2 offset)
{
if ((growthDirections & GrowthDirections.Centered) != 0)
{
if ((growthDirections & GrowthDirections.Up) != 0 || (growthDirections & GrowthDirections.Down) != 0)
{
int elementsPerRow = (int)(size.X / (iconSize.X + iconPadding.X));
long elementsInRow = Math.Min(elementsPerRow, elementCount - (elementsPerRow * row));
direction.X = 1;
direction.Y = (growthDirections & GrowthDirections.Down) != 0 ? 1 : -1;
offset.X = -(iconSize.X + iconPadding.X) * elementsInRow / 2f;
offset.Y = direction.Y == 1 ? 0 : -iconSize.Y;
}
else// if ((growthDirections & GrowthDirections.Left) != 0 || (growthDirections & GrowthDirections.Right) != 0)
{
int elementsPerCol = (int)(size.Y / (iconSize.Y + iconPadding.Y));
long elementsInCol = Math.Min(elementsPerCol, elementCount - (elementsPerCol * col));
direction.X = (growthDirections & GrowthDirections.Left) != 0 ? -1 : 1;
direction.Y = 1;
offset.X = direction.X == 1 ? 0 : -iconSize.X;
offset.Y = -(iconSize.Y + iconPadding.Y) * elementsInCol / 2f;
}
}
else
{
direction.X = (growthDirections & GrowthDirections.Right) != 0 ? 1 : -1;
direction.Y = (growthDirections & GrowthDirections.Down) != 0 ? 1 : -1;
offset.X = direction.X == 1 ? 0 : -iconSize.X;
offset.Y = direction.Y == 1 ? 0 : -iconSize.Y;
}
}
public static Vector2 CalculateStartPosition(Vector2 position, Vector2 size, GrowthDirections growthDirections)
{
Vector2 area = size;
if ((growthDirections & GrowthDirections.Left) != 0)
{
area.X = -area.X;
}
if ((growthDirections & GrowthDirections.Up) != 0)
{
area.Y = -area.Y;
}
Vector2 startPos = position;
if ((growthDirections & GrowthDirections.Centered) != 0)
{
if ((growthDirections & GrowthDirections.Up) != 0 || (growthDirections & GrowthDirections.Down) != 0)
{
startPos.X = position.X - size.X / 2f;
}
else if ((growthDirections & GrowthDirections.Left) != 0 || (growthDirections & GrowthDirections.Right) != 0)
{
startPos.Y = position.Y - size.Y / 2f;
}
}
Vector2 endPos = position + area;
if (endPos.X < position.X)
{
startPos.X = endPos.X;
}
if (endPos.Y < position.Y)
{
startPos.Y = endPos.Y;
}
return startPos;
}
public static (List<Vector2>, Vector2, Vector2) CalculateIconPositions(
GrowthDirections directions,
uint count,
Vector2 position,
Vector2 size,
Vector2 iconSize,
Vector2 iconPadding,
bool fillRowsFirst,
LayoutInfo layoutInfo)
{
List<Vector2> list = new List<Vector2>();
Vector2 minPos = new Vector2(float.MaxValue, float.MaxValue);
Vector2 maxPos = Vector2.Zero;
int row = 0;
int col = 0;
for (int i = 0; i < count; i++)
{
CalculateAxisDirections(
directions,
row,
col,
count,
size,
iconSize,
iconPadding,
out Vector2 direction,
out Vector2 offset
);
Vector2 pos = new Vector2(
position.X + offset.X + iconSize.X * col * direction.X + iconPadding.X * col * direction.X,
position.Y + offset.Y + iconSize.Y * row * direction.Y + iconPadding.Y * row * direction.Y
);
minPos.X = Math.Min(pos.X, minPos.X);
minPos.Y = Math.Min(pos.Y, minPos.Y);
maxPos.X = Math.Max(pos.X + iconSize.X, maxPos.X);
maxPos.Y = Math.Max(pos.Y + iconSize.Y, maxPos.Y);
list.Add(pos);
// rows / columns
if (fillRowsFirst)
{
col += 1;
if (col >= layoutInfo.TotalColCount)
{
col = 0;
row += 1;
}
}
else
{
row += 1;
if (row >= layoutInfo.TotalRowCount)
{
row = 0;
col += 1;
}
}
}
return (list, minPos, maxPos);
}
}
[Flags]
public enum GrowthDirections : short
{
Up = 1,
Down = 2,
Left = 4,
Right = 8,
Centered = 16,
}
}