Merge FlexContainer, WIP dynamic container resizing.
This commit is contained in:
@@ -15,7 +15,15 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
||||
/// <inheritdoc />
|
||||
public Rect MinimumRect { get; set; } = Rect.Zero;
|
||||
/// <inheritdoc />
|
||||
public Rect Size { get; set; } = Rect.Zero;
|
||||
public Rect Size
|
||||
{
|
||||
get => _size;
|
||||
set
|
||||
{
|
||||
_size = value;
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public bool Visible { get; set; } = true;
|
||||
/// <inheritdoc />
|
||||
@@ -71,7 +79,7 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
||||
{
|
||||
if (Children.Count == 0)
|
||||
{
|
||||
Size = MinimumRect;
|
||||
_size = MinimumRect;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,7 +107,7 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
||||
float finalWidth = MathF.Max(occupiedWidth, MinimumRect.Width);
|
||||
float finalHeight = MathF.Max(occupiedHeight, MinimumRect.Height);
|
||||
|
||||
Size = new Rect(finalWidth, finalHeight);
|
||||
MinimumRect = new Rect(finalWidth, finalHeight);
|
||||
}
|
||||
|
||||
public void AddChild(IElement child)
|
||||
@@ -135,4 +143,5 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
|
||||
|
||||
private List<IElement> _children = new();
|
||||
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