Unify Containers and Widgets by creating a base UIElement, add more anchor types, make anchor calculations an extension of Anchor.

This commit is contained in:
2025-06-22 23:28:30 +02:00
parent 95ae2de7ac
commit 61ac079f2b
9 changed files with 163 additions and 181 deletions

View File

@@ -7,46 +7,17 @@ namespace Voile.UI.Containers;
/// <summary>
/// A base class for all UI containers, used to position and rendering child <see cref="IElement">s.
/// </summary>
public abstract class Container : IElement, IParentableElement, IUpdatableElement, IResizeableElement, IRenderableElement, IAnchorableElement
public abstract class Container : UIElement, IParentableElement
{
/// <inheritdoc />
public IReadOnlyList<IElement> Children => _children;
/// <inheritdoc />
public Vector2 Position { get; set; }
/// <inheritdoc />
public Rect MinimumRect { get; set; } = Rect.Zero;
/// <inheritdoc />
public Rect Size
{
get => _size;
set
{
_size = value;
if (value.Width < MinimumRect.Width)
{
_size.Width = MinimumRect.Width;
}
if (value.Height < MinimumRect.Height)
{
_size.Height = MinimumRect.Height;
}
MarkDirty();
}
}
/// <inheritdoc />
public bool Visible { get; set; } = true;
/// <inheritdoc />
public bool Dirty => _isDirty;
/// <summary>
/// Specifies if this <see cref="Container"/>'s minimum size will be confined to contents.
/// </summary>
public bool ConfineToContents { get; set; } = false;
public Anchor Anchor { get; set; }
public Vector2 AnchorOffset { get; set; }
public override Rect MinimumSize => _minimumSize;
public Container()
{
@@ -55,28 +26,26 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
public Container(Rect minimumSize)
{
MinimumRect = minimumSize;
_minimumSize = minimumSize;
MarkDirty();
}
public Container(Rect minimumSize, List<IElement> children)
{
MinimumRect = minimumSize;
_minimumSize = minimumSize;
_children = children;
MarkDirty();
}
public void Update()
protected override void OnUpdate()
{
if (!_isDirty) return;
_isDirty = false;
foreach (var child in _children)
{
if (child is not IUpdatableElement updatable) continue;
updatable.MarkDirty();
updatable.Update();
if (child is IAnchorableElement anchorable)
@@ -93,8 +62,6 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
}
}
public void MarkDirty() => _isDirty = true;
/// <summary>
/// Called when this <see cref="Container"/> has to rearrange its children.
/// </summary>
@@ -127,14 +94,14 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
float occupiedWidth = (maxX - minX) + padding * 2;
float occupiedHeight = (maxY - minY) + padding * 2;
float finalWidth = MathF.Max(occupiedWidth, MinimumRect.Width);
float finalHeight = MathF.Max(occupiedHeight, MinimumRect.Height);
float finalWidth = MathF.Max(occupiedWidth, _minimumSize.Width);
float finalHeight = MathF.Max(occupiedHeight, _minimumSize.Height);
MinimumRect = new Rect(finalWidth, finalHeight);
Size = new Rect(finalWidth, finalHeight);
if (MinimumRect > _size)
if (_minimumSize > Size)
{
_size = MinimumRect;
Size = _minimumSize;
}
}
@@ -154,7 +121,7 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
Update();
}
public void Render(RenderSystem renderer, Style style)
public override void Render(RenderSystem renderer, Style style)
{
foreach (var child in Children)
{
@@ -163,45 +130,6 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
}
}
public void DrawSize(RenderSystem renderer)
{
renderer.SetTransform(Position, Vector2.Zero);
renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f);
}
public void ApplyAnchor(Vector2 parentPosition, Rect parentRect)
{
var absoluteOffset = AnchorOffset * new Vector2(Size.Width, Size.Height);
switch (Anchor)
{
case Anchor.TopLeft:
Position = parentPosition - AnchorOffset;
break;
case Anchor.TopCenter:
var topCenterX = parentRect.Width / 2;
var topCenterY = parentPosition.Y;
Position = new Vector2(parentPosition.X + topCenterX, topCenterY) - absoluteOffset;
break;
case Anchor.TopRight:
Position = new Vector2(parentPosition.X + parentRect.Width, parentPosition.Y) - absoluteOffset;
break;
case Anchor.Fill:
Position = parentPosition;
Size = parentRect;
break;
default:
throw new NotImplementedException("This anchor type is not implemented!");
}
MarkDirty();
Update();
}
private List<IElement> _children = new();
private bool _isDirty;
private Rect _size = Rect.Zero;
private Rect _minimumSize = Rect.Zero;
}