Add debug rectangle size rendering, auto-resize containers to fit all children.

This commit is contained in:
2025-06-20 18:57:36 +02:00
parent 1b09d80f7a
commit 3154b3fa10
12 changed files with 114 additions and 40 deletions

View File

@@ -20,6 +20,7 @@ public class TestGame : Game
InitializeSystemsDefault(); InitializeSystemsDefault();
_uiSystem = new UISystem(new ResourceRef<Style>(Guid.Empty)); _uiSystem = new UISystem(new ResourceRef<Style>(Guid.Empty));
_uiSystem.RenderDebugRects = true;
_particleSystem = new ParticleSystem(); _particleSystem = new ParticleSystem();

View File

@@ -187,6 +187,17 @@ namespace Voile.Rendering
}, transformPivot, transformRotation, VoileColorToRaylibColor(color)); }, transformPivot, transformRotation, VoileColorToRaylibColor(color));
} }
public override void DrawRectangleOutline(Vector2 size, Color color, float outlineWidth = 1)
{
Raylib.DrawRectangleLinesEx(new Rectangle()
{
X = transformPosition.X,
Y = transformPosition.Y,
Width = size.X,
Height = size.Y
}, outlineWidth, VoileColorToRaylibColor(color));
}
public override void DrawDebugText(string text, int fontSize, Color color) public override void DrawDebugText(string text, int fontSize, Color color)
{ {
Raylib.DrawText(text, (int)transformPosition.X, (int)transformPosition.Y, fontSize, VoileColorToRaylibColor(color)); Raylib.DrawText(text, (int)transformPosition.X, (int)transformPosition.Y, fontSize, VoileColorToRaylibColor(color));

View File

@@ -180,6 +180,9 @@ namespace Voile.Rendering
/// <param name="size">Rectangle size.</param> /// <param name="size">Rectangle size.</param>
/// <param name="color">Fill color.</param> /// <param name="color">Fill color.</param>
public abstract void DrawRectangle(Vector2 size, Color color); public abstract void DrawRectangle(Vector2 size, Color color);
public abstract void DrawRectangleOutline(Vector2 size, Color color, float outlineWidth = 1.0f);
/// <summary> /// <summary>
/// Draws a debug text with a default font. /// Draws a debug text with a default font.
/// </summary> /// </summary>

View File

@@ -399,6 +399,11 @@ namespace Voile.Rendering
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override void DrawRectangleOutline(Vector2 size, Color color, float outlineWidth = 1)
{
throw new NotImplementedException();
}
private Vector2 _windowSize = Vector2.Zero; private Vector2 _windowSize = Vector2.Zero;
private IWindow? _window; private IWindow? _window;

View File

@@ -1,29 +1,40 @@
using System.Numerics; using System.Numerics;
using Voile.Rendering;
namespace Voile.UI.Containers; namespace Voile.UI.Containers;
/// <summary> /// <summary>
/// A base class for all UI containers, used to position and rendering child <see cref="IElement">s. /// A base class for all UI containers, used to position and rendering child <see cref="IElement">s.
/// </summary> /// </summary>
public abstract class Container : IElement, IParentableElement, IUpdatableElement public abstract class Container : IElement, IParentableElement, IUpdatableElement, IResizeableElement, IRenderableElement
{ {
public IReadOnlyList<IElement> Children => _children; public IReadOnlyList<IElement> Children => _children;
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
public Rect MinimumRect { get; set; } = Rect.Zero;
public Rect Size { get; set; } = Rect.Zero; public Rect Size { get; set; } = Rect.Zero;
public bool Visible { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public Container() public Container()
{ {
} }
public Container(List<IElement> children) public Container(Rect minimumSize)
{ {
MinimumRect = minimumSize;
}
public Container(Rect minimumSize, List<IElement> children)
{
MinimumRect = minimumSize;
_children = children; _children = children;
} }
public void Update() public void Update()
{ {
Arrange(); Arrange();
CalculateMinimumSize();
foreach (var child in _children) foreach (var child in _children)
{ {
if (child is not IUpdatableElement updatable) continue; if (child is not IUpdatableElement updatable) continue;
@@ -32,11 +43,59 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
} }
public abstract void Arrange(); public abstract void Arrange();
public void CalculateMinimumSize()
{
if (Children.Count == 0)
{
Size = MinimumRect;
return;
}
float minX = float.MaxValue;
float minY = float.MaxValue;
float maxX = float.MinValue;
float maxY = float.MinValue;
foreach (var child in Children)
{
var pos = child.Position;
var size = child.Size;
minX = MathF.Min(minX, pos.X);
minY = MathF.Min(minY, pos.Y);
maxX = MathF.Max(maxX, pos.X + size.Width);
maxY = MathF.Max(maxY, pos.Y + size.Height);
}
// Optionally apply padding
float padding = 0f; // or make this configurable
Size = new Rect(
maxX - minX + padding * 2,
maxY - minY + padding * 2
);
}
public void AddChild(IElement child) public void AddChild(IElement child)
{ {
_children.Add(child); _children.Add(child);
Update(); Update();
} }
public void Render(RenderSystem renderer, Style style)
{
foreach (var child in Children)
{
if (child is not IRenderableElement renderable) continue;
renderable.Render(renderer, style);
}
}
public void DrawSize(RenderSystem renderer)
{
renderer.SetTransform(Position, Vector2.Zero);
renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f);
}
private List<IElement> _children = new(); private List<IElement> _children = new();
} }

View File

@@ -3,15 +3,14 @@ using Voile.Rendering;
namespace Voile.UI.Containers; namespace Voile.UI.Containers;
public class GridContainer : Container, IRenderableElement public class GridContainer : Container
{ {
public int Columns { get; set; } = 2; public int Columns { get; set; } = 2;
public float ColumnSpacing { get; set; } = 16.0f; public float ColumnSpacing { get; set; } = 16.0f;
public float RowSpacing { get; set; } = 16.0f; public float RowSpacing { get; set; } = 16.0f;
public bool Visible { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public GridContainer(List<IElement> children, int columns = 2, float colSpacing = 16.0f, float rowSpacing = 16.0f) public GridContainer(Rect minimumSize, List<IElement> children, int columns = 2, float colSpacing = 16.0f, float rowSpacing = 16.0f)
: base(children) : base(minimumSize, children)
{ {
Columns = columns; Columns = columns;
ColumnSpacing = colSpacing; ColumnSpacing = colSpacing;
@@ -58,13 +57,4 @@ public class GridContainer : Container, IRenderableElement
} }
} }
} }
public void Render(RenderSystem renderer, Style style)
{
foreach (var child in Children)
{
if (child is not IRenderableElement renderable) continue;
renderable.Render(renderer, style);
}
}
} }

View File

@@ -3,12 +3,11 @@ using Voile.Rendering;
namespace Voile.UI.Containers; namespace Voile.UI.Containers;
public class HorizontalContainer : Container, IRenderableElement public class HorizontalContainer : Container
{ {
public float Spacing { get; set; } = 16.0f; public float Spacing { get; set; } = 16.0f;
public bool Visible { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public HorizontalContainer(List<IElement> children, float spacing = 16.0f) : base(children) public HorizontalContainer(Rect minimumSize, List<IElement> children, float spacing = 16.0f) : base(minimumSize, children)
{ {
Spacing = spacing; Spacing = spacing;
} }
@@ -36,13 +35,4 @@ public class HorizontalContainer : Container, IRenderableElement
} }
} }
} }
public void Render(RenderSystem renderer, Style style)
{
foreach (var child in Children)
{
if (child is not IRenderableElement renderable) continue;
renderable.Render(renderer, style);
}
}
} }

View File

@@ -3,12 +3,11 @@ using Voile.Rendering;
namespace Voile.UI.Containers; namespace Voile.UI.Containers;
public class VerticalContainer : Container, IRenderableElement public class VerticalContainer : Container
{ {
public float Spacing { get; set; } = 16.0f; public float Spacing { get; set; } = 16.0f;
public bool Visible { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public VerticalContainer(List<IElement> children, float spacing = 16.0f) : base(children) public VerticalContainer(Rect minimumSize, List<IElement> children, float spacing = 16.0f) : base(minimumSize, children)
{ {
Spacing = spacing; Spacing = spacing;
} }
@@ -36,13 +35,4 @@ public class VerticalContainer : Container, IRenderableElement
} }
} }
} }
public void Render(RenderSystem renderer, Style style)
{
foreach (var child in Children)
{
if (child is not IRenderableElement renderable) continue;
renderable.Render(renderer, style);
}
}
} }

View File

@@ -33,6 +33,7 @@ public interface IRenderableElement
{ {
public bool Visible { get; set; } public bool Visible { get; set; }
public void Render(RenderSystem renderer, Style style); public void Render(RenderSystem renderer, Style style);
public void DrawSize(RenderSystem renderer);
} }
public interface IInputElement public interface IInputElement

View File

@@ -1,3 +1,4 @@
using System.Numerics;
using Voile.Rendering; using Voile.Rendering;
using Voile.UI.Containers; using Voile.UI.Containers;
@@ -7,6 +8,8 @@ public class UISystem : IUpdatableSystem, IRenderableSystem
{ {
public IReadOnlyList<IElement> Elements => _elements; public IReadOnlyList<IElement> Elements => _elements;
public bool RenderDebugRects { get; set; }
public UISystem(ResourceRef<Style> style) public UISystem(ResourceRef<Style> style)
{ {
_style = style; _style = style;
@@ -37,6 +40,20 @@ public class UISystem : IUpdatableSystem, IRenderableSystem
if (element is IRenderableElement renderable) if (element is IRenderableElement renderable)
{ {
renderable.Render(renderer, _style.Value); renderable.Render(renderer, _style.Value);
if (!RenderDebugRects) return;
renderable.DrawSize(renderer);
}
if (element is IParentableElement parentable)
{
foreach (var child in parentable.Children)
{
if (child is not IRenderableElement renderableChild) continue;
if (!RenderDebugRects) return;
renderableChild.DrawSize(renderer);
}
} }
} }
} }

View File

@@ -32,6 +32,7 @@ public abstract class Widget : IElement, IRenderableElement, IInputElement, IRes
/// </summary> /// </summary>
/// <param name="renderer"></param> /// <param name="renderer"></param>
public abstract void Render(RenderSystem renderer, Style style); public abstract void Render(RenderSystem renderer, Style style);
/// <summary> /// <summary>
/// Called when this widget receives input. /// Called when this widget receives input.
/// </summary> /// </summary>
@@ -45,4 +46,10 @@ public abstract class Widget : IElement, IRenderableElement, IInputElement, IRes
Size = MinimumRect; Size = MinimumRect;
} }
} }
public void DrawSize(RenderSystem renderer)
{
renderer.SetTransform(Position, Vector2.Zero);
renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f);
}
} }

View File

@@ -15,7 +15,7 @@
<PackageReference Include="SoLoud.NET" Version="2020.2.7.1" /> <PackageReference Include="SoLoud.NET" Version="2020.2.7.1" />
<PackageReference Include="Tommy" Version="3.1.2" /> <PackageReference Include="Tommy" Version="3.1.2" />
<PackageReference Include="ImGui.NET" Version="1.89.4" /> <PackageReference Include="ImGui.NET" Version="1.89.4" />
<PackageReference Include="Raylib-cs" Version="6.1.1" /> <PackageReference Include="Raylib-cs" Version="7.0.1" />
<PackageReference Include="SharpFont" Version="4.0.1" /> <PackageReference Include="SharpFont" Version="4.0.1" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta19" /> <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta19" />
<PackageReference Include="StbImageSharp" Version="2.27.13" /> <PackageReference Include="StbImageSharp" Version="2.27.13" />