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:
@@ -1,5 +1,4 @@
|
||||
using System.Numerics;
|
||||
using System.Xml.Serialization;
|
||||
using Voile.Input;
|
||||
using Voile.Rendering;
|
||||
|
||||
@@ -8,13 +7,8 @@ namespace Voile.UI.Widgets;
|
||||
/// <summary>
|
||||
/// A base class for all UI widgets.
|
||||
/// </summary>
|
||||
public abstract class Widget : IElement, IRenderableElement, IInputElement, IResizeableElement, IUpdatableElement, IAnchorableElement
|
||||
public abstract class Widget : UIElement, IInputElement
|
||||
{
|
||||
public bool Visible { get; set; } = true;
|
||||
public bool IgnoreInput { get; set; }
|
||||
public Vector2 Position { get; set; } = Vector2.Zero;
|
||||
public Rect Size { get; set; } = new();
|
||||
|
||||
public Widget()
|
||||
{
|
||||
MarkDirty();
|
||||
@@ -32,90 +26,18 @@ public abstract class Widget : IElement, IRenderableElement, IInputElement, IRes
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
/// </inheritdoc>
|
||||
public abstract Rect MinimumRect { get; }
|
||||
|
||||
public bool Dirty => _isDirty;
|
||||
|
||||
public Anchor Anchor { get; set; }
|
||||
public Vector2 AnchorOffset { get; set; }
|
||||
|
||||
public void ApplyAnchor(Vector2 parentPosition, Rect parentRect)
|
||||
{
|
||||
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) + AnchorOffset;
|
||||
break;
|
||||
case Anchor.TopRight:
|
||||
Position = new Vector2(parentPosition.X + parentRect.Width, parentPosition.Y);
|
||||
break;
|
||||
|
||||
case Anchor.Fill:
|
||||
Position = parentPosition;
|
||||
Size = parentRect;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException("This anchor type is not implemented!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when its time to draw this widget.
|
||||
/// </summary>
|
||||
/// <param name="renderer"></param>
|
||||
public abstract void Render(RenderSystem renderer, Style style);
|
||||
|
||||
public void Input(UIInputContext context)
|
||||
{
|
||||
if (context.Handled) return;
|
||||
OnInput(context);
|
||||
if (context.Handled || IgnoreInput) return;
|
||||
if (ContainsPoint(context.MousePosition))
|
||||
{
|
||||
OnInput(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this widget receives input.
|
||||
/// </summary>
|
||||
/// <param name="action">An input action this widget received.</param>
|
||||
protected abstract void OnInput(UIInputContext action);
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!_isDirty) return;
|
||||
_isDirty = false;
|
||||
|
||||
if (Size == Rect.Zero)
|
||||
{
|
||||
Size = MinimumRect;
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkDirty() => _isDirty = true;
|
||||
|
||||
public void DrawSize(RenderSystem renderer)
|
||||
{
|
||||
renderer.SetTransform(Position, Vector2.Zero);
|
||||
renderer.DrawRectangleOutline(new Vector2(Size.Width, Size.Height), Color.Red, 2.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this <see cref="Widget"/> contains a point within its confines.
|
||||
/// </summary>
|
||||
/// <param name="pointPosition">A global position of the point.</param>
|
||||
/// <returns>True if the point is inside the widget; otherwise, false.</returns>
|
||||
public bool ContainsPoint(Vector2 pointPosition)
|
||||
{
|
||||
return pointPosition.X >= Position.X &&
|
||||
pointPosition.Y >= Position.Y &&
|
||||
pointPosition.X <= Position.X + Size.Width &&
|
||||
pointPosition.Y <= Position.Y + Size.Height;
|
||||
}
|
||||
|
||||
private bool _isDirty = true;
|
||||
protected abstract void OnInput(UIInputContext context);
|
||||
}
|
||||
Reference in New Issue
Block a user