Merge FlexContainer, WIP dynamic container resizing.
This commit is contained in:
@@ -125,5 +125,13 @@ public class TestGame : Game
|
|||||||
private ResourceRef<Sound> _sound;
|
private ResourceRef<Sound> _sound;
|
||||||
private ResourceRef<Texture2d> _icon;
|
private ResourceRef<Texture2d> _icon;
|
||||||
|
|
||||||
private GridContainer _container = new(minimumSize: new Rect(64.0f, 64.0f), new());
|
private FlexContainer _container = new(minimumSize: new Rect(64.0f, 64.0f), new())
|
||||||
|
{
|
||||||
|
Size = new Rect(500, 300),
|
||||||
|
Direction = FlexDirection.Row,
|
||||||
|
Justify = JustifyContent.Start,
|
||||||
|
Align = AlignItems.Center,
|
||||||
|
Wrap = true,
|
||||||
|
Gap = 10f
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,15 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Rect MinimumRect { get; set; } = Rect.Zero;
|
public Rect MinimumRect { get; set; } = Rect.Zero;
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Rect Size { get; set; } = Rect.Zero;
|
public Rect Size
|
||||||
|
{
|
||||||
|
get => _size;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_size = value;
|
||||||
|
MarkDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Visible { get; set; } = true;
|
public bool Visible { get; set; } = true;
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -71,7 +79,7 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
|||||||
{
|
{
|
||||||
if (Children.Count == 0)
|
if (Children.Count == 0)
|
||||||
{
|
{
|
||||||
Size = MinimumRect;
|
_size = MinimumRect;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +107,7 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
|||||||
float finalWidth = MathF.Max(occupiedWidth, MinimumRect.Width);
|
float finalWidth = MathF.Max(occupiedWidth, MinimumRect.Width);
|
||||||
float finalHeight = MathF.Max(occupiedHeight, MinimumRect.Height);
|
float finalHeight = MathF.Max(occupiedHeight, MinimumRect.Height);
|
||||||
|
|
||||||
Size = new Rect(finalWidth, finalHeight);
|
MinimumRect = new Rect(finalWidth, finalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddChild(IElement child)
|
public void AddChild(IElement child)
|
||||||
@@ -135,4 +143,5 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
|||||||
|
|
||||||
private List<IElement> _children = new();
|
private List<IElement> _children = new();
|
||||||
private bool _isDirty;
|
private bool _isDirty;
|
||||||
|
private Rect _size = Rect.Zero;
|
||||||
}
|
}
|
||||||
143
Voile/Source/UI/Containers/FlexContainer.cs
Normal file
143
Voile/Source/UI/Containers/FlexContainer.cs
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Voile.Rendering;
|
||||||
|
|
||||||
|
namespace Voile.UI.Containers;
|
||||||
|
|
||||||
|
public enum FlexDirection
|
||||||
|
{
|
||||||
|
Row,
|
||||||
|
Column
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum JustifyContent
|
||||||
|
{
|
||||||
|
Start,
|
||||||
|
Center,
|
||||||
|
End,
|
||||||
|
SpaceBetween
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AlignItems
|
||||||
|
{
|
||||||
|
Start,
|
||||||
|
Center,
|
||||||
|
End
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FlexContainer : Container
|
||||||
|
{
|
||||||
|
public FlexDirection Direction { get; set; } = FlexDirection.Row;
|
||||||
|
public JustifyContent Justify { get; set; } = JustifyContent.Start;
|
||||||
|
public AlignItems Align { get; set; } = AlignItems.Start;
|
||||||
|
public bool Wrap { get; set; } = false;
|
||||||
|
public float Gap { get; set; } = 16f;
|
||||||
|
|
||||||
|
public bool DistributeRemainingSpace { get; set; } = false;
|
||||||
|
|
||||||
|
public FlexContainer() : base() { }
|
||||||
|
|
||||||
|
public FlexContainer(Rect minimumSize, List<IElement> children) : base(minimumSize, children) { }
|
||||||
|
|
||||||
|
public override void Arrange()
|
||||||
|
{
|
||||||
|
float containerMainSize = (Direction == FlexDirection.Row) ? Size.Width : Size.Height;
|
||||||
|
float mainPos = (Direction == FlexDirection.Row) ? Position.X : Position.Y;
|
||||||
|
float crossPos = (Direction == FlexDirection.Row) ? Position.Y : Position.X;
|
||||||
|
|
||||||
|
List<List<IElement>> lines = new();
|
||||||
|
List<IElement> currentLine = new();
|
||||||
|
float lineMainSum = 0f;
|
||||||
|
float maxCross = 0f;
|
||||||
|
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
Vector2 childSize = GetChildSize(child);
|
||||||
|
float mainSize = GetMainSize(childSize);
|
||||||
|
float crossSize = GetCrossSize(childSize);
|
||||||
|
|
||||||
|
bool wrapLine = Wrap && currentLine.Count > 0 && (lineMainSum + mainSize + Gap > containerMainSize);
|
||||||
|
|
||||||
|
if (wrapLine)
|
||||||
|
{
|
||||||
|
lines.Add(currentLine);
|
||||||
|
currentLine = new();
|
||||||
|
lineMainSum = 0f;
|
||||||
|
maxCross += crossSize + Gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLine.Add(child);
|
||||||
|
lineMainSum += mainSize + (currentLine.Count > 1 ? Gap : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLine.Count > 0)
|
||||||
|
{
|
||||||
|
lines.Add(currentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentCross = crossPos;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
float lineMainLength = line.Sum(child => GetMainSize(GetChildSize(child))) + Gap * (line.Count - 1);
|
||||||
|
float justifyOffset = GetJustifyOffset(containerMainSize, lineMainLength, line.Count);
|
||||||
|
|
||||||
|
float currentMain = mainPos + justifyOffset;
|
||||||
|
float maxLineCross = line.Select(child => GetCrossSize(GetChildSize(child))).Max();
|
||||||
|
|
||||||
|
foreach (var child in line)
|
||||||
|
{
|
||||||
|
Vector2 childSize = GetChildSize(child);
|
||||||
|
|
||||||
|
float alignedCross = currentCross + GetAlignOffset(maxLineCross, GetCrossSize(childSize));
|
||||||
|
|
||||||
|
Vector2 childPos = (Direction == FlexDirection.Row)
|
||||||
|
? new Vector2(currentMain, alignedCross)
|
||||||
|
: new Vector2(alignedCross, currentMain);
|
||||||
|
|
||||||
|
child.Position = childPos;
|
||||||
|
|
||||||
|
currentMain += GetMainSize(childSize) + Gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCross += maxLineCross + Gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 GetChildSize(IElement child)
|
||||||
|
{
|
||||||
|
if (child is IResizeableElement resizeable)
|
||||||
|
return new Vector2(resizeable.MinimumRect.Width, resizeable.MinimumRect.Height);
|
||||||
|
return new Vector2(child.Size.Width, child.Size.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetMainSize(Vector2 size)
|
||||||
|
{
|
||||||
|
return Direction == FlexDirection.Row ? size.X : size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetCrossSize(Vector2 size)
|
||||||
|
{
|
||||||
|
return Direction == FlexDirection.Row ? size.Y : size.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetJustifyOffset(float containerSize, float totalMain, int itemCount)
|
||||||
|
{
|
||||||
|
return Justify switch
|
||||||
|
{
|
||||||
|
JustifyContent.Center => (containerSize - totalMain) / 2f,
|
||||||
|
JustifyContent.End => containerSize - totalMain,
|
||||||
|
JustifyContent.SpaceBetween => (itemCount > 1) ? 0 : (containerSize - totalMain) / 2f,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetAlignOffset(float maxCross, float childCross)
|
||||||
|
{
|
||||||
|
return Align switch
|
||||||
|
{
|
||||||
|
AlignItems.Center => (maxCross - childCross) / 2f,
|
||||||
|
AlignItems.End => maxCross - childCross,
|
||||||
|
_ => 0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user