171 lines
5.3 KiB
C#
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;
|
|
} |