WIP: dirty flag for UI.

This commit is contained in:
2025-06-20 19:34:10 +02:00
parent a9a8113dd9
commit 76dafe9996
3 changed files with 89 additions and 3 deletions

View File

@@ -8,41 +8,65 @@ namespace Voile.UI.Containers;
/// </summary> /// </summary>
public abstract class Container : IElement, IParentableElement, IUpdatableElement, IResizeableElement, IRenderableElement public abstract class Container : IElement, IParentableElement, IUpdatableElement, IResizeableElement, IRenderableElement
{ {
/// <inheritdoc />
public IReadOnlyList<IElement> Children => _children; public IReadOnlyList<IElement> Children => _children;
/// <inheritdoc />
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
/// <inheritdoc />
public Rect MinimumRect { get; set; } = Rect.Zero; public Rect MinimumRect { get; set; } = Rect.Zero;
/// <inheritdoc />
public Rect Size { get; set; } = Rect.Zero; public Rect Size { get; set; } = Rect.Zero;
/// <inheritdoc />
public bool Visible { get; set; } = true; public bool Visible { get; set; } = true;
/// <inheritdoc />
public bool Dirty => _isDirty;
public Container() public Container()
{ {
MarkDirty();
} }
public Container(Rect minimumSize) public Container(Rect minimumSize)
{ {
MinimumRect = minimumSize; MinimumRect = minimumSize;
MarkDirty();
} }
public Container(Rect minimumSize, List<IElement> children) public Container(Rect minimumSize, List<IElement> children)
{ {
MinimumRect = minimumSize; MinimumRect = minimumSize;
_children = children; _children = children;
MarkDirty();
} }
public void Update() public void Update()
{ {
Arrange(); if (!_isDirty) return;
CalculateMinimumSize(); _isDirty = false;
foreach (var child in _children) foreach (var child in _children)
{ {
if (child is not IUpdatableElement updatable) continue; if (child is not IUpdatableElement updatable) continue;
updatable.Update(); updatable.Update();
} }
Arrange();
CalculateMinimumSize();
} }
public void MarkDirty() => _isDirty = true;
/// <summary>
/// Called when this <see cref="Container"/> has to rearrange its children.
/// </summary>
public abstract void Arrange(); public abstract void Arrange();
/// <summary>
/// Recalculates a minimum size for this <see cref="Container"/>.
/// </summary>
public void CalculateMinimumSize() public void CalculateMinimumSize()
{ {
if (Children.Count == 0) if (Children.Count == 0)
@@ -81,12 +105,16 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
public void AddChild(IElement child) public void AddChild(IElement child)
{ {
_children.Add(child); _children.Add(child);
MarkDirty();
Update(); Update();
} }
public void RemoveChild(IElement child) public void RemoveChild(IElement child)
{ {
_children.Remove(child); _children.Remove(child);
MarkDirty();
Update(); Update();
} }
@@ -106,4 +134,5 @@ public abstract class Container : IElement, IParentableElement, IUpdatableElemen
} }
private List<IElement> _children = new(); private List<IElement> _children = new();
private bool _isDirty;
} }

View File

@@ -6,14 +6,31 @@ namespace Voile.UI;
public interface IElement public interface IElement
{ {
/// <summary>
/// This element's position in pixels relative to the viewport top-left edge.
/// </summary>
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
/// <summary>
/// The size of this element.
/// </summary>
public Rect Size { get; set; } public Rect Size { get; set; }
} }
public interface IParentableElement public interface IParentableElement
{ {
/// <summary>
/// This parentable element's children.
/// </summary>
public IReadOnlyList<IElement> Children { get; } public IReadOnlyList<IElement> Children { get; }
/// <summary>
/// Add a child element to this element.
/// </summary>
/// <param name="child">Child <see cref="IElement"/>.</param>
public void AddChild(IElement child); public void AddChild(IElement child);
/// <summary>
/// Remove a child element from this element.
/// </summary>
/// <param name="child">Child <see cref="IElement"/> to remove.</param>
public void RemoveChild(IElement child); public void RemoveChild(IElement child);
} }
@@ -27,18 +44,48 @@ public interface IResizeableElement
public interface IUpdatableElement public interface IUpdatableElement
{ {
/// <summary>
/// Specifies if this element's properties have changed, making it necessary to update it.
/// </summary>
public bool Dirty { get; }
/// <summary>
/// Update this element.
/// </summary>
void Update(); void Update();
/// <summary>
/// Marks this element as changed, requiring an update.
/// </summary>
void MarkDirty();
} }
public interface IRenderableElement public interface IRenderableElement
{ {
/// <summary>
/// Specifies if this element should be drawn.
/// </summary>
public bool Visible { get; set; } public bool Visible { get; set; }
/// <summary>
/// Render this element.
/// </summary>
/// <param name="renderer">Renderer to call draw operations on.</param>
/// <param name="style">A style to use to draw this element.</param>
public void Render(RenderSystem renderer, Style style); public void Render(RenderSystem renderer, Style style);
/// <summary>
/// Draws this element's size bounds.
/// </summary>
/// <param name="renderer">Renderer to use.</param>
public void DrawSize(RenderSystem renderer); public void DrawSize(RenderSystem renderer);
} }
public interface IInputElement public interface IInputElement
{ {
/// <summary>
/// Specifies if this element should ignore inputs.
/// </summary>
public bool IgnoreInput { get; set; } public bool IgnoreInput { get; set; }
/// <summary>
/// Send an input action to this element.
/// </summary>
/// <param name="action">Input action to send.</param>
void Input(IInputAction action); void Input(IInputAction action);
} }

View File

@@ -22,11 +22,14 @@ public abstract class Widget : IElement, IRenderableElement, IInputElement, IRes
public Widget(Vector2 position) public Widget(Vector2 position)
{ {
Position = position; Position = position;
Size = MinimumRect;
} }
/// </inheritdoc> /// </inheritdoc>
public abstract Rect MinimumRect { get; } public abstract Rect MinimumRect { get; }
public bool Dirty => _isDirty;
/// <summary> /// <summary>
/// Called when its time to draw this widget. /// Called when its time to draw this widget.
/// </summary> /// </summary>
@@ -41,15 +44,22 @@ public abstract class Widget : IElement, IRenderableElement, IInputElement, IRes
public void Update() public void Update()
{ {
if (!_isDirty) return;
_isDirty = false;
if (Size == Rect.Zero) if (Size == Rect.Zero)
{ {
Size = MinimumRect; Size = MinimumRect;
} }
} }
public void MarkDirty() => _isDirty = true;
public void DrawSize(RenderSystem renderer) public void DrawSize(RenderSystem renderer)
{ {
renderer.SetTransform(Position, Vector2.Zero); renderer.SetTransform(Position, Vector2.Zero);
renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f); renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f);
} }
private bool _isDirty = true;
} }