Files
Voile/Voile/Source/UI/UISystem.cs

171 lines
5.3 KiB
C#

using System.Numerics;
using Voile.Input;
using Voile.Rendering;
namespace Voile.UI;
public class UISystem : IUpdatableSystem, IRenderableSystem
{
public IReadOnlyList<IElement> Elements => _elements;
public bool RenderDebugRects { get; set; }
public Color DebugSizeRectColor { get; set; } = Color.Red;
public Color DebugDirtyRectColor { get; set; } = new Color(1.0f, 1.0f, 0.0f, 0.5f);
public UISystem(InputSystem inputSystem)
{
_style = ResourceRef<Style>.Empty();
_input = inputSystem;
}
public UISystem(InputSystem inputSystem, ResourceRef<Style> style)
{
_input = inputSystem;
_style = style;
}
public UISystem(InputSystem inputSystem, ResourceRef<Style> style, List<UIElement> elements)
{
_input = inputSystem;
_style = style;
_elements = elements;
}
public void AddElement(UIElement element) => _elements.Add(element);
public void RemoveElement(UIElement element) => _elements.Remove(element);
public void Update(double deltaTime)
{
HandleInput();
}
public void Render(RenderSystem renderer)
{
// Update elements each time UI system is rendered.
foreach (var element in _elements)
{
if (element is not IUpdatableElement updatable) continue;
updatable.Update();
}
foreach (var element in _elements)
{
if (element is IRenderableElement renderable)
{
// TODO: normally you'd load a default style if the one supplied is empty,
// but for now this will do.
if (!_style.TryGetValue(out var value))
{
value = new Style(string.Empty);
}
renderable.Render(renderer, value);
}
}
if (!RenderDebugRects) return;
foreach (var element in _elements)
{
if (element is not UIElement uiElement) continue;
DrawDebugForElement(renderer, uiElement);
}
}
private void DrawDebugForElement(RenderSystem renderer, UIElement element)
{
var size = new Vector2(element.Size.Width, element.Size.Height);
renderer.SetTransform(element.GlobalPosition, Vector2.Zero);
renderer.DrawRectangleOutline(size, DebugSizeRectColor);
if (element.Dirty)
{
renderer.DrawRectangle(size, DebugDirtyRectColor);
}
if (element is IParentableElement parentableElement)
{
foreach (var child in parentableElement.Children)
{
if (child is not UIElement childElement) continue;
DrawDebugForElement(renderer, childElement);
}
}
}
private void HandleInput()
{
int charPressed = _input.GetCharPressed();
Vector2 mousePos = _input.GetMousePosition();
Vector2 currentMousePosition = _input.GetMousePosition();
foreach (var (actionName, mappings) in InputSystem.InputMappings)
{
foreach (var action in mappings)
{
if (action.IsPressed(_input))
{
// TODO: specify which mouse button is used in the context.
var context = new UIInputContext(action, mousePos, actionName, charPressed)
{
MouseDown = _input.IsMouseButtonDown(MouseButton.Left),
MouseReleased = _input.IsMouseButtonReleased(MouseButton.Left),
MousePressed = _input.IsMouseButtonReleased(MouseButton.Left),
};
if (PropagateInput(_elements, context))
return;
}
}
}
if (charPressed != 0)
{
var context = new UIInputContext(new KeyInputAction(KeyboardKey.Null), mousePos, "", charPressed);
PropagateInput(_elements, context);
}
if (currentMousePosition != _lastMousePosition)
{
// TODO: specify which mouse button is used in the context.
var context = new UIInputContext(new MouseInputAction(MouseButton.Left), mousePos, "", charPressed)
{
MouseDown = _input.IsMouseButtonDown(MouseButton.Left),
MouseReleased = _input.IsMouseButtonReleased(MouseButton.Left),
MousePressed = _input.IsMouseButtonReleased(MouseButton.Left),
};
PropagateInput(_elements, context);
}
}
private bool PropagateInput(List<UIElement> elements, UIInputContext context)
{
for (int i = elements.Count - 1; i >= 0; i--)
{
var element = elements[i];
if (element is IInputElement inputElement && !inputElement.IgnoreInput)
{
inputElement.Input(context);
_input.SetAsHandled();
// return true;
}
if (element is IParentableElement parent)
{
if (PropagateInput(parent.Children.ToList(), context))
return true;
}
}
return false;
}
private ResourceRef<Style> _style;
private List<UIElement> _elements = new();
private InputSystem _input;
private Vector2 _lastMousePosition = Vector2.Zero;
}