Files
Voile/Voile/Source/UI/Widgets/Button.cs

190 lines
4.1 KiB
C#

using System.Numerics;
using Voile.Rendering;
using Voile.Resources;
namespace Voile.UI.Widgets;
/// <summary>
/// A clickable button with a label.
/// </summary>
public class Button : Widget
{
public enum ButtonState
{
Disabled,
Hovered,
Pressed,
Focused,
Normal
}
public string Text
{
get => _text; set
{
_text = value;
MarkDirty(DirtyFlags.Content);
}
}
public ButtonState CurrentState { get; private set; } = ButtonState.Normal;
public override Rect MinimumSize => Padding + _textSize;
/// <summary>
/// <see cref="FontSet"/> to use with this button.
/// </summary>
public FontSet FontSet { get; set; } = new();
public Size Padding
{
get => _padding; set
{
_padding = value;
}
}
public override string? StyleElementName => nameof(Button);
public override string[]? StyleModifiers =>
[
CurrentState.ToString()
];
public Button(string text, ResourceRef<Font> fontOverride, Action pressedAction)
{
_text = text;
_pressedAction = pressedAction;
FontSet.AddFont(fontOverride);
MarkDirty();
}
public Button(string text, FontSet fontSet)
{
_text = text;
FontSet = fontSet;
MarkDirty();
}
public Button(string text, FontSet fontSet, Action pressedAction)
{
_text = text;
_pressedAction = pressedAction;
FontSet = fontSet;
MarkDirty();
}
protected override void OnRender(RenderSystem renderer, Style style)
{
_padding = style.Padding ?? Voile.Size.Zero;
var color = style.TextColor ?? Color.Black;
var pos = new Vector2(
GlobalPosition.X + _padding.Left,
GlobalPosition.Y + _padding.Top);
renderer.SetTransform(pos, Vector2.Zero);
if (_font.HasValue)
{
renderer.DrawText(_font, _text, color);
}
}
protected virtual void Pressed() { }
protected override void OnInput(UIInputContext action)
{
bool isHovering = ContainsPoint(action.MousePosition);
if (action.MousePressed && isHovering)
{
_isHeldDown = true;
CurrentState = ButtonState.Pressed;
}
else if (action.MouseReleased)
{
if (_isHeldDown && isHovering)
{
_pressedAction?.Invoke();
Pressed();
}
_isHeldDown = false;
CurrentState = isHovering ? ButtonState.Hovered : ButtonState.Normal;
}
else
{
if (_isHeldDown)
{
CurrentState = ButtonState.Pressed; // keep showing as pressed
}
else if (isHovering)
{
CurrentState = ButtonState.Hovered;
}
else
{
CurrentState = ButtonState.Normal;
}
}
}
protected override void OnUpdate(LayoutContext layoutContext)
{
ResolveFont();
if (!_font.HasValue)
return;
var font = _font.Value;
var newSize = font.Measure(_text);
_textSize = newSize;
var final = _textSize + _padding;
if (_cachedSize.Width == final.Width &&
_cachedSize.Height == final.Height)
return;
_cachedSize = final;
Size = final;
MarkDirty(DirtyFlags.Layout);
}
private void ResolveFont()
{
if (_font.HasValue)
return;
var text = _text;
for (int i = 0; i < text.Length; i++)
{
if (FontSet.TryGetFontFor(text[i], out var f))
{
_font = f;
break;
}
}
}
private Action? _pressedAction;
private Rect _cachedSize = Rect.Zero;
private string _text = "Hello, World!";
private Rect _textSize = Rect.Zero;
private ResourceRef<Font> _font = ResourceRef<Font>.Empty();
private Size _padding;
private bool _isHeldDown;
}