Compare commits
10 Commits
207c8a20a4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 11423d86e5 | |||
| 828ec4973f | |||
| bef27762ee | |||
| 8a3ed42bb7 | |||
| 52bbf5a9e1 | |||
| 49aa3f071f | |||
| 6c0e6707ee | |||
| c6e1bf7f41 | |||
| 5d3a2c2222 | |||
| e1e965796b |
3
TODO.md
3
TODO.md
@@ -98,7 +98,8 @@
|
|||||||
- Styling
|
- Styling
|
||||||
- ~~Style sheet~~
|
- ~~Style sheet~~
|
||||||
- ~~Add style settings for UI panels (for buttons, labels, etc.).~~
|
- ~~Add style settings for UI panels (for buttons, labels, etc.).~~
|
||||||
- Parse StyleSheet from TOML file.
|
- ~~Parse StyleSheet from TOML file.~~
|
||||||
- Animated styles
|
- Animated styles
|
||||||
|
- (stretch goal) Style variables
|
||||||
- Find a way to reference external assets in the style (fonts, textures).
|
- Find a way to reference external assets in the style (fonts, textures).
|
||||||
- Create a default style for widgets.
|
- Create a default style for widgets.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[Button]
|
[Button]
|
||||||
BackgroundColor = "#0f62fe"
|
BackgroundColor = "#0f62fe"
|
||||||
TextColor = "#37eb34"
|
TextColor = "#ffffff"
|
||||||
Padding = 16.0
|
Padding = 16.0
|
||||||
|
|
||||||
# Creates an empty rule for Button.Normal.
|
# Creates an empty rule for Button.Normal.
|
||||||
@@ -29,7 +29,6 @@ BackgroundColor = "#750e13"
|
|||||||
BackgroundColor = [0, 0, 0, 0]
|
BackgroundColor = [0, 0, 0, 0]
|
||||||
BorderSize = 1.0
|
BorderSize = 1.0
|
||||||
BorderColor = "#0f62fe"
|
BorderColor = "#0f62fe"
|
||||||
TextColor = "#0353e9"
|
|
||||||
|
|
||||||
[Button.Outline.Normal]
|
[Button.Outline.Normal]
|
||||||
TextColor = "#0353e9"
|
TextColor = "#0353e9"
|
||||||
@@ -41,6 +40,24 @@ BackgroundColor = "#0353e9"
|
|||||||
BackgroundColor = "#002d9c"
|
BackgroundColor = "#002d9c"
|
||||||
BorderColor = [0, 0, 0, 0]
|
BorderColor = [0, 0, 0, 0]
|
||||||
|
|
||||||
|
[Button.Link]
|
||||||
|
BackgroundColor = [0, 0, 0, 0]
|
||||||
|
TextColor = "#0f62fe"
|
||||||
|
Padding = 0.0
|
||||||
|
|
||||||
|
[Button.Link.Normal]
|
||||||
|
|
||||||
|
[Button.Link.Hovered]
|
||||||
|
BorderColor = "#0043ce"
|
||||||
|
TextColor = "#0043ce"
|
||||||
|
BorderSize = [0, 0, 0, 1]
|
||||||
|
|
||||||
|
[Button.Link.Pressed]
|
||||||
|
TextColor = "#161616"
|
||||||
|
BorderSize = [0, 0, 0, 1]
|
||||||
|
BorderColor = "#161616"
|
||||||
|
|
||||||
|
|
||||||
# Default background color for all Container derived classes.
|
# Default background color for all Container derived classes.
|
||||||
[Container]
|
[Container]
|
||||||
BackgroundColor = "#e0e0e0"
|
BackgroundColor = "#e0e0e0"
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class TestGame : Game
|
|||||||
_uiSystem = new UISystem(Input);
|
_uiSystem = new UISystem(Input);
|
||||||
// _uiSystem.RenderDebugRects = true;
|
// _uiSystem.RenderDebugRects = true;
|
||||||
|
|
||||||
|
ResourceManager.EnableFileWatching();
|
||||||
|
|
||||||
_particleSystem = new ParticleSystem();
|
_particleSystem = new ParticleSystem();
|
||||||
|
|
||||||
AddSystemToUpdate(_uiSystem);
|
AddSystemToUpdate(_uiSystem);
|
||||||
@@ -80,8 +82,9 @@ public class TestGame : Game
|
|||||||
var outlineButton = new Button("Outline button", _defaultFontSet);
|
var outlineButton = new Button("Outline button", _defaultFontSet);
|
||||||
outlineButton.StyleVariant = "Outline";
|
outlineButton.StyleVariant = "Outline";
|
||||||
|
|
||||||
_buttonContainer.AddChild(addButton);
|
var linkButton = new Button("Link button", _defaultFontSet);
|
||||||
_buttonContainer.AddChild(removeButton);
|
linkButton.StyleVariant = "Link";
|
||||||
|
|
||||||
|
|
||||||
var c = new HorizontalContainer()
|
var c = new HorizontalContainer()
|
||||||
{
|
{
|
||||||
@@ -93,6 +96,7 @@ public class TestGame : Game
|
|||||||
c.AddChild(addButton);
|
c.AddChild(addButton);
|
||||||
c.AddChild(removeButton);
|
c.AddChild(removeButton);
|
||||||
c.AddChild(outlineButton);
|
c.AddChild(outlineButton);
|
||||||
|
c.AddChild(linkButton);
|
||||||
|
|
||||||
var vc = new VerticalContainer(0.0f);
|
var vc = new VerticalContainer(0.0f);
|
||||||
vc.AddChild(c);
|
vc.AddChild(c);
|
||||||
@@ -111,7 +115,7 @@ public class TestGame : Game
|
|||||||
{
|
{
|
||||||
if (Input.IsActionPressed("reload"))
|
if (Input.IsActionPressed("reload"))
|
||||||
{
|
{
|
||||||
ResourceManager.Reload();
|
// ResourceManager.Reload();
|
||||||
// _particleSystem!.RestartEmitter(_emitterId);
|
// _particleSystem!.RestartEmitter(_emitterId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,23 @@ namespace Voile;
|
|||||||
|
|
||||||
public class GridSet<T>
|
public class GridSet<T>
|
||||||
{
|
{
|
||||||
public float GridSize { get; }
|
/// <summary>
|
||||||
public GridSet(float gridSize = 32.0f)
|
/// The size of a cell of this <see cref="GridSet"/>.
|
||||||
|
/// </summary>
|
||||||
|
public float CellSize { get; }
|
||||||
|
public GridSet(float cellSize = 32.0f)
|
||||||
{
|
{
|
||||||
GridSize = gridSize;
|
CellSize = cellSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an element to this <see cref="GridSet"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">Position of the element in this <see cref="GridSet"/>.</param>
|
||||||
|
/// <param name="child">Element to add.</param>
|
||||||
public void Add(Vector2 position, T child)
|
public void Add(Vector2 position, T child)
|
||||||
{
|
{
|
||||||
var snap = Vector2.One * GridSize;
|
var snap = Vector2.One * CellSize;
|
||||||
position = position.Snapped(snap);
|
position = position.Snapped(snap);
|
||||||
|
|
||||||
if (_values.TryGetValue(position, out var list))
|
if (_values.TryGetValue(position, out var list))
|
||||||
@@ -28,10 +36,11 @@ public class GridSet<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(T child)
|
/// <summary>
|
||||||
{
|
/// Removes an element from this <see cref="GridSet"/>.
|
||||||
|
/// </summary>
|
||||||
}
|
/// <param name="child">Element to remove.</param>
|
||||||
|
public void Remove(T child) => throw new NotImplementedException();
|
||||||
|
|
||||||
private Dictionary<Vector2, List<T>> _values = new();
|
private Dictionary<Vector2, List<T>> _values = new();
|
||||||
}
|
}
|
||||||
@@ -65,6 +65,9 @@ namespace Voile.Resources.DataReaders
|
|||||||
/// <param name="defaultValue">Default value in case this getter fails to get data.</param>
|
/// <param name="defaultValue">Default value in case this getter fails to get data.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
double GetDouble(string key, double defaultValue = 0.0);
|
double GetDouble(string key, double defaultValue = 0.0);
|
||||||
|
|
||||||
|
string GetString(string key, string defaultValue);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a Voile.Color from this data getter.
|
/// Get a Voile.Color from this data getter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -80,6 +83,14 @@ namespace Voile.Resources.DataReaders
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Vector2 GetVector2(string key, Vector2 defaultValue);
|
Vector2 GetVector2(string key, Vector2 defaultValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a <see cref="Size"/> from this data getter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key of the value.</param>
|
||||||
|
/// <param name="defaultValue">Default value in case this getter fails to get data.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Size GetSize(string key, Size defaultValue);
|
||||||
|
|
||||||
T[] GetArray<T>(string key, T[] defaultValue);
|
T[] GetArray<T>(string key, T[] defaultValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,21 @@ public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValue
|
|||||||
public double GetDouble(string key, double defaultValue = 0)
|
public double GetDouble(string key, double defaultValue = 0)
|
||||||
=> TryGetNode(key, out var node) && node.IsFloat ? node.AsFloat : defaultValue;
|
=> TryGetNode(key, out var node) && node.IsFloat ? node.AsFloat : defaultValue;
|
||||||
|
|
||||||
|
public string GetString(string key, string defaultValue)
|
||||||
|
{
|
||||||
|
if (!TryGetNode(key, out var node))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.IsString)
|
||||||
|
{
|
||||||
|
return node.AsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
public Color GetColor(string key, Color defaultValue)
|
public Color GetColor(string key, Color defaultValue)
|
||||||
{
|
{
|
||||||
if (!TryGetNode(key, out var node))
|
if (!TryGetNode(key, out var node))
|
||||||
@@ -143,7 +158,7 @@ public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValue
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Color can only be represented as an array of integers in the range of 0-255, array of floats (0-1), hexadecimal, or hex string.");
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +171,39 @@ public class TomlDataReader : IStreamDataReader, IDataValidator, IStreamKeyValue
|
|||||||
return new Vector2(arr[0], arr[1]);
|
return new Vector2(arr[0], arr[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Size GetSize(string key, Size defaultValue)
|
||||||
|
{
|
||||||
|
if (!TryGetNode(key, out var node))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if (node.IsInteger)
|
||||||
|
{
|
||||||
|
return new Size(node.AsInteger);
|
||||||
|
}
|
||||||
|
else if (node.IsFloat)
|
||||||
|
{
|
||||||
|
return new Size(node.AsFloat);
|
||||||
|
}
|
||||||
|
else if (node.IsArray)
|
||||||
|
{
|
||||||
|
var sizeArray = node.AsArray;
|
||||||
|
|
||||||
|
var lNode = sizeArray[0];
|
||||||
|
var rNode = sizeArray[1];
|
||||||
|
var tNode = sizeArray[2];
|
||||||
|
var bNode = sizeArray[3];
|
||||||
|
|
||||||
|
var l = lNode.IsInteger ? lNode.AsInteger : 0;
|
||||||
|
var t = tNode.IsInteger ? tNode.AsInteger : 0;
|
||||||
|
var r = rNode.IsInteger ? rNode.AsInteger : 0;
|
||||||
|
var b = bNode.IsInteger ? bNode.AsInteger : 0;
|
||||||
|
|
||||||
|
return new Size(l, r, t, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
public T[] GetArray<T>(string key, T[] defaultValue)
|
public T[] GetArray<T>(string key, T[] defaultValue)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("Generic array reading not implemented yet.");
|
throw new NotImplementedException("Generic array reading not implemented yet.");
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class Font : Resource, IUpdatableResource, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float totalHeight = maxAscent + maxDescent;
|
float totalHeight = Size;
|
||||||
return new Rect(totalWidth, totalHeight);
|
return new Rect(totalWidth, totalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,7 +269,6 @@ namespace Voile.Resources
|
|||||||
| NotifyFilters.CreationTime
|
| NotifyFilters.CreationTime
|
||||||
| NotifyFilters.DirectoryName
|
| NotifyFilters.DirectoryName
|
||||||
| NotifyFilters.FileName
|
| NotifyFilters.FileName
|
||||||
| NotifyFilters.LastAccess
|
|
||||||
| NotifyFilters.LastWrite
|
| NotifyFilters.LastWrite
|
||||||
| NotifyFilters.Security
|
| NotifyFilters.Security
|
||||||
| NotifyFilters.Size;
|
| NotifyFilters.Size;
|
||||||
|
|||||||
@@ -140,10 +140,8 @@ public abstract class Container : UIElement, IParentableElement
|
|||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(RenderSystem renderer, Style style)
|
protected override void OnRender(RenderSystem renderer, Style style)
|
||||||
{
|
{
|
||||||
RenderStyleBox(renderer, style);
|
|
||||||
|
|
||||||
foreach (var child in Children)
|
foreach (var child in Children)
|
||||||
{
|
{
|
||||||
if (child is not IRenderableElement renderable) continue;
|
if (child is not IRenderableElement renderable) continue;
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ public class FillContainer : Container
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(RenderSystem renderer, Style style)
|
protected override void OnRender(RenderSystem renderer, Style style)
|
||||||
{
|
{
|
||||||
base.Render(renderer, style);
|
base.OnRender(renderer, style);
|
||||||
|
|
||||||
Rect parentSize;
|
Rect parentSize;
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public interface IUpdatableElement
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update this element.
|
/// Update this element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Update();
|
void Update(float dt = 0.0f);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks this element as changed, requiring an update.
|
/// Marks this element as changed, requiring an update.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -11,15 +11,43 @@ namespace Voile.UI;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Style
|
public class Style
|
||||||
{
|
{
|
||||||
|
public enum AnimationType
|
||||||
|
{
|
||||||
|
Linear,
|
||||||
|
EaseIn,
|
||||||
|
EaseOut,
|
||||||
|
EaseInOut
|
||||||
|
}
|
||||||
|
|
||||||
|
public float TransitionDuration = 0f;
|
||||||
|
public AnimationType TransitionType = AnimationType.Linear;
|
||||||
|
|
||||||
public Style() { }
|
public Style() { }
|
||||||
|
|
||||||
public Size Padding { get; set; }
|
public Size? Padding { get; set; }
|
||||||
public Color? BackgroundColor { get; set; }
|
public Color? BackgroundColor { get; set; }
|
||||||
public Size BorderSize { get; set; }
|
public Size? BorderSize { get; set; }
|
||||||
public Color? BorderColor { get; set; }
|
public Color? BorderColor { get; set; }
|
||||||
public float CornerRadius { get; set; }
|
public float CornerRadius { get; set; }
|
||||||
public Color TextColor { get; set; } = Color.White;
|
public Color? TextColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Merges this <see cref="Style"/> with a different one.<br />
|
||||||
|
/// Properties that are not set for this <see cref="Style"/> will be inherited from <paramref name="overrideStyle"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="overrideStyle"></param>
|
||||||
|
/// <returns>A merged <see cref="Style"/>.</returns>
|
||||||
|
public Style Merge(Style overrideStyle)
|
||||||
|
{
|
||||||
|
return new Style
|
||||||
|
{
|
||||||
|
BackgroundColor = overrideStyle.BackgroundColor != default ? overrideStyle.BackgroundColor : BackgroundColor,
|
||||||
|
TextColor = overrideStyle.TextColor != default ? overrideStyle.TextColor : TextColor,
|
||||||
|
Padding = overrideStyle.Padding != default ? overrideStyle.Padding : Padding,
|
||||||
|
BorderSize = overrideStyle.BorderSize != default ? overrideStyle.BorderSize : BorderSize,
|
||||||
|
BorderColor = overrideStyle.BorderColor != default ? overrideStyle.BorderColor : BorderColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StyleSheetLoader : ResourceLoader<StyleSheet>
|
public class StyleSheetLoader : ResourceLoader<StyleSheet>
|
||||||
@@ -60,6 +88,14 @@ public class StyleSheetLoader : ResourceLoader<StyleSheet>
|
|||||||
{
|
{
|
||||||
var style = new Style();
|
var style = new Style();
|
||||||
|
|
||||||
|
string easingName = reader.GetString("TransitionType", "Linear");
|
||||||
|
|
||||||
|
if (!Enum.TryParse<Style.AnimationType>(easingName, true, out var easing))
|
||||||
|
easing = Style.AnimationType.Linear;
|
||||||
|
|
||||||
|
style.TransitionType = easing;
|
||||||
|
|
||||||
|
|
||||||
if (reader.HasKey("BackgroundColor"))
|
if (reader.HasKey("BackgroundColor"))
|
||||||
style.BackgroundColor = reader.GetColor("BackgroundColor", Color.Transparent);
|
style.BackgroundColor = reader.GetColor("BackgroundColor", Color.Transparent);
|
||||||
|
|
||||||
@@ -67,10 +103,10 @@ public class StyleSheetLoader : ResourceLoader<StyleSheet>
|
|||||||
style.TextColor = reader.GetColor("TextColor", Color.Black);
|
style.TextColor = reader.GetColor("TextColor", Color.Black);
|
||||||
|
|
||||||
if (reader.HasKey("Padding"))
|
if (reader.HasKey("Padding"))
|
||||||
style.Padding = new Size(reader.GetFloat("Padding"));
|
style.Padding = reader.GetSize("Padding", Size.Zero);
|
||||||
|
|
||||||
if (reader.HasKey("BorderSize"))
|
if (reader.HasKey("BorderSize"))
|
||||||
style.BorderSize = new Size(reader.GetFloat("BorderSize"));
|
style.BorderSize = reader.GetSize("BorderSize", Size.Zero);
|
||||||
|
|
||||||
if (reader.HasKey("BorderColor"))
|
if (reader.HasKey("BorderColor"))
|
||||||
style.BorderColor = reader.GetColor("BorderColor", Color.Transparent);
|
style.BorderColor = reader.GetColor("BorderColor", Color.Transparent);
|
||||||
@@ -88,25 +124,13 @@ public class StyleSheetLoader : ResourceLoader<StyleSheet>
|
|||||||
var subKey = string.Join('.', parts.Take(i));
|
var subKey = string.Join('.', parts.Take(i));
|
||||||
if (allStyles.TryGetValue(subKey, out var parentStyle))
|
if (allStyles.TryGetValue(subKey, out var parentStyle))
|
||||||
{
|
{
|
||||||
merged = MergeStyles(merged, parentStyle);
|
merged = merged.Merge(parentStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Style MergeStyles(Style baseStyle, Style overrideStyle)
|
|
||||||
{
|
|
||||||
return new Style
|
|
||||||
{
|
|
||||||
BackgroundColor = overrideStyle.BackgroundColor != default ? overrideStyle.BackgroundColor : baseStyle.BackgroundColor,
|
|
||||||
TextColor = overrideStyle.TextColor != default ? overrideStyle.TextColor : baseStyle.TextColor,
|
|
||||||
Padding = overrideStyle.Padding != default ? overrideStyle.Padding : baseStyle.Padding,
|
|
||||||
BorderSize = overrideStyle.BorderSize != default ? overrideStyle.BorderSize : baseStyle.BorderSize,
|
|
||||||
BorderColor = overrideStyle.BorderColor != default ? overrideStyle.BorderColor : baseStyle.BorderColor,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly TomlDataReader _reader = new();
|
private readonly TomlDataReader _reader = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
56
Voile/Source/UI/StyleAnimator.cs
Normal file
56
Voile/Source/UI/StyleAnimator.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
namespace Voile.UI;
|
||||||
|
|
||||||
|
public class StyleAnimator
|
||||||
|
{
|
||||||
|
public bool IsComplete => _elapsed >= _duration;
|
||||||
|
|
||||||
|
public StyleAnimator(Style from, Style to, float duration)
|
||||||
|
{
|
||||||
|
_from = from;
|
||||||
|
_to = to;
|
||||||
|
|
||||||
|
_duration = duration;
|
||||||
|
_elapsed = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Ease(float t, Style.AnimationType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
Style.AnimationType.Linear => t,
|
||||||
|
Style.AnimationType.EaseIn => t * t,
|
||||||
|
Style.AnimationType.EaseOut => t * (2 - t),
|
||||||
|
Style.AnimationType.EaseInOut => t < 0.5f
|
||||||
|
? 2 * t * t
|
||||||
|
: -1 + (4 - 2 * t) * t,
|
||||||
|
_ => t
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Style Update(float deltaTime)
|
||||||
|
{
|
||||||
|
_elapsed = MathF.Min(_elapsed + deltaTime, _duration);
|
||||||
|
float t = _duration == 0 ? 1 : _elapsed / _duration;
|
||||||
|
float easedT = Ease(t, _to.TransitionType);
|
||||||
|
|
||||||
|
return LerpStyle(_from, _to, easedT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Style LerpStyle(Style from, Style to, float t)
|
||||||
|
{
|
||||||
|
var result = new Style()
|
||||||
|
{
|
||||||
|
BackgroundColor = MathUtils.LerpColor(from.BackgroundColor ?? Color.Transparent, to.BackgroundColor ?? Color.Transparent, t),
|
||||||
|
TextColor = MathUtils.LerpColor(from.TextColor ?? Color.Black, to.TextColor ?? Color.Black, t),
|
||||||
|
Padding = MathUtils.LerpSize(from.Padding ?? Size.Zero, to.Padding ?? Size.Zero, t),
|
||||||
|
BorderColor = MathUtils.LerpColor(from.BorderColor ?? Color.Transparent, to.BorderColor ?? Color.Transparent, t),
|
||||||
|
BorderSize = MathUtils.LerpSize(from.BorderSize ?? Size.Zero, to.BorderSize ?? Size.Zero, t),
|
||||||
|
TransitionType = to.TransitionType
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Style _from, _to;
|
||||||
|
private float _duration, _elapsed;
|
||||||
|
}
|
||||||
@@ -91,7 +91,7 @@ public abstract class UIElement : IElement, IRenderableElement, IResizeableEleme
|
|||||||
MarkDirty();
|
MarkDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update(float dt = 0.0f)
|
||||||
{
|
{
|
||||||
if (!_dirty) return;
|
if (!_dirty) return;
|
||||||
_dirty = false;
|
_dirty = false;
|
||||||
@@ -107,7 +107,13 @@ public abstract class UIElement : IElement, IRenderableElement, IResizeableEleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Render(RenderSystem renderer, Style style);
|
public void Render(RenderSystem renderer, Style style)
|
||||||
|
{
|
||||||
|
RenderStyleBox(renderer, style);
|
||||||
|
OnRender(renderer, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void OnRender(RenderSystem renderer, Style style);
|
||||||
protected abstract void OnUpdate();
|
protected abstract void OnUpdate();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -121,44 +127,49 @@ public abstract class UIElement : IElement, IRenderableElement, IResizeableEleme
|
|||||||
var borderColor = style.BorderColor ?? Color.Transparent;
|
var borderColor = style.BorderColor ?? Color.Transparent;
|
||||||
var borderSize = style.BorderSize;
|
var borderSize = style.BorderSize;
|
||||||
|
|
||||||
|
var borderLeft = borderSize?.Left ?? 0;
|
||||||
|
var borderRight = borderSize?.Right ?? 0;
|
||||||
|
var borderTop = borderSize?.Top ?? 0;
|
||||||
|
var borderBottom = borderSize?.Bottom ?? 0;
|
||||||
|
|
||||||
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
||||||
|
|
||||||
renderer.DrawRectangle(new Vector2(Size.Width, Size.Height), backgroundColor);
|
renderer.DrawRectangle(new Vector2(Size.Width, Size.Height), backgroundColor);
|
||||||
|
|
||||||
if (borderSize.Left > 0)
|
if (borderLeft > 0)
|
||||||
{
|
{
|
||||||
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
||||||
renderer.DrawRectangle(
|
renderer.DrawRectangle(
|
||||||
new Vector2(borderSize.Left, Size.Height),
|
new Vector2(borderLeft, Size.Height),
|
||||||
borderColor
|
borderColor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (borderSize.Top > 0)
|
if (borderTop > 0)
|
||||||
{
|
{
|
||||||
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
||||||
renderer.DrawRectangle(
|
renderer.DrawRectangle(
|
||||||
new Vector2(Size.Width, borderSize.Top),
|
new Vector2(Size.Width, borderTop),
|
||||||
borderColor
|
borderColor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (borderSize.Right > 0)
|
if (borderRight > 0)
|
||||||
{
|
{
|
||||||
var rightX = GlobalPosition.X + Size.Width - borderSize.Right;
|
var rightX = GlobalPosition.X + Size.Width - borderRight;
|
||||||
renderer.SetTransform(new Vector2(rightX, GlobalPosition.Y), Vector2.Zero);
|
renderer.SetTransform(new Vector2(rightX, GlobalPosition.Y), Vector2.Zero);
|
||||||
renderer.DrawRectangle(
|
renderer.DrawRectangle(
|
||||||
new Vector2(borderSize.Right, Size.Height),
|
new Vector2(borderRight, Size.Height),
|
||||||
borderColor
|
borderColor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (borderSize.Bottom > 0)
|
if (borderBottom > 0)
|
||||||
{
|
{
|
||||||
var bottomY = GlobalPosition.Y + Size.Height - borderSize.Bottom;
|
var bottomY = GlobalPosition.Y + Size.Height - borderBottom;
|
||||||
renderer.SetTransform(new Vector2(GlobalPosition.X, bottomY), Vector2.Zero);
|
renderer.SetTransform(new Vector2(GlobalPosition.X, bottomY), Vector2.Zero);
|
||||||
renderer.DrawRectangle(
|
renderer.DrawRectangle(
|
||||||
new Vector2(Size.Width, borderSize.Bottom),
|
new Vector2(Size.Width, borderBottom),
|
||||||
borderColor
|
borderColor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,48 @@ using Voile.Input;
|
|||||||
|
|
||||||
namespace Voile.UI;
|
namespace Voile.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Input information for UI elements.
|
||||||
|
/// </summary>
|
||||||
public class UIInputContext
|
public class UIInputContext
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current action handled by this <see cref="UIElement"/>.
|
||||||
|
/// </summary>
|
||||||
public IInputAction Action { get; }
|
public IInputAction Action { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Current mouse position.
|
||||||
|
/// </summary>
|
||||||
public Vector2 MousePosition { get; }
|
public Vector2 MousePosition { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if a mouse button was pressed.
|
||||||
|
/// </summary>
|
||||||
public bool MousePressed { get; set; }
|
public bool MousePressed { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if a mouse button was released.
|
||||||
|
/// </summary>
|
||||||
public bool MouseReleased { get; set; }
|
public bool MouseReleased { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if a mouse button is currently held.
|
||||||
|
/// </summary>
|
||||||
public bool MouseDown { get; set; }
|
public bool MouseDown { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the current <see cref="IInputAction"/>.
|
||||||
|
/// </summary>
|
||||||
public string ActionName { get; }
|
public string ActionName { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Keycode of a currently pressed character.
|
||||||
|
/// </summary>
|
||||||
public int CharPressed { get; }
|
public int CharPressed { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if this <see cref="UIInputContext"/> registered any character input from keyboard.
|
||||||
|
/// </summary>
|
||||||
public bool HasCharInput => CharPressed != 0;
|
public bool HasCharInput => CharPressed != 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if this context's input was already handled and no longer needs to be processed.
|
||||||
|
/// </summary>
|
||||||
public bool Handled => _handled;
|
public bool Handled => _handled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -81,26 +81,23 @@ public class Button : Widget
|
|||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(RenderSystem renderer, Style style)
|
protected override void OnRender(RenderSystem renderer, Style style)
|
||||||
{
|
{
|
||||||
if (_padding != style.Padding)
|
if (_padding != style.Padding)
|
||||||
{
|
{
|
||||||
MarkDirty();
|
MarkDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
_padding = style.Padding;
|
_padding = style.Padding ?? Voile.Size.Zero;
|
||||||
var textColor = style.TextColor;
|
var textColor = style.TextColor ?? Color.Black;
|
||||||
|
|
||||||
// renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
|
||||||
// renderer.DrawRectangle(new Vector2(Size.Width, Size.Height), backgroundColor);
|
|
||||||
|
|
||||||
RenderStyleBox(renderer, style);
|
|
||||||
|
|
||||||
var textPosition = new Vector2(GlobalPosition.X + Padding.Left, GlobalPosition.Y + Padding.Top);
|
var textPosition = new Vector2(GlobalPosition.X + Padding.Left, GlobalPosition.Y + Padding.Top);
|
||||||
renderer.SetTransform(textPosition, Vector2.Zero);
|
renderer.SetTransform(textPosition, Vector2.Zero);
|
||||||
renderer.DrawText(_suitableFont, _text, textColor);
|
renderer.DrawText(_suitableFont, _text, textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void Pressed() { }
|
||||||
|
|
||||||
protected override void OnInput(UIInputContext action)
|
protected override void OnInput(UIInputContext action)
|
||||||
{
|
{
|
||||||
bool isHovering = ContainsPoint(action.MousePosition);
|
bool isHovering = ContainsPoint(action.MousePosition);
|
||||||
@@ -115,6 +112,7 @@ public class Button : Widget
|
|||||||
if (_isHeldDown && isHovering)
|
if (_isHeldDown && isHovering)
|
||||||
{
|
{
|
||||||
_pressedAction?.Invoke();
|
_pressedAction?.Invoke();
|
||||||
|
Pressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
_isHeldDown = false;
|
_isHeldDown = false;
|
||||||
|
|||||||
@@ -50,12 +50,10 @@ public class Label : Widget
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(RenderSystem renderer, Style style)
|
protected override void OnRender(RenderSystem renderer, Style style)
|
||||||
{
|
{
|
||||||
RenderStyleBox(renderer, style);
|
|
||||||
|
|
||||||
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
||||||
renderer.DrawText(_suitableFont, _text, style.TextColor);
|
renderer.DrawText(_suitableFont, _text, style.TextColor ?? Color.Black);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnUpdate()
|
protected override void OnUpdate()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class RectangleWidget : Widget
|
|||||||
_hoverColor = color.Lightened(0.25f);
|
_hoverColor = color.Lightened(0.25f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Render(RenderSystem renderer, Style style)
|
protected override void OnRender(RenderSystem renderer, Style style)
|
||||||
{
|
{
|
||||||
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
renderer.SetTransform(GlobalPosition, Vector2.Zero);
|
||||||
renderer.DrawRectangle(new Vector2(Size.Width, Size.Height), Color);
|
renderer.DrawRectangle(new Vector2(Size.Width, Size.Height), Color);
|
||||||
|
|||||||
@@ -15,6 +15,18 @@ namespace Voile
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
public static float Lerp(float a, float b, float t) => t <= 0f ? a : t >= 1f ? b : LerpUnclamped(a, b, t);
|
public static float Lerp(float a, float b, float t) => t <= 0f ? a : t >= 1f ? b : LerpUnclamped(a, b, t);
|
||||||
|
|
||||||
|
public static Size LerpSize(Size a, Size b, float t)
|
||||||
|
{
|
||||||
|
t = Math.Clamp(t, 0f, 1f);
|
||||||
|
|
||||||
|
float left = Lerp(a.Left, b.Left, t);
|
||||||
|
float right = Lerp(a.Right, b.Right, t);
|
||||||
|
float top = Lerp(a.Top, b.Top, t);
|
||||||
|
float bottom = Lerp(a.Bottom, b.Bottom, t);
|
||||||
|
|
||||||
|
return new Size(left, right, top, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
public static Color LerpColor(Color colorA, Color colorB, float t)
|
public static Color LerpColor(Color colorA, Color colorB, float t)
|
||||||
{
|
{
|
||||||
t = Math.Clamp(t, 0f, 1f);
|
t = Math.Clamp(t, 0f, 1f);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class FileSystemFile : VirtualFile
|
|||||||
|
|
||||||
public override Stream GetStream()
|
public override Stream GetStream()
|
||||||
{
|
{
|
||||||
return new FileStream(_fsPath, FileMode.Open, FileAccess.Read);
|
return new FileStream(_fsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _fsPath;
|
private string _fsPath;
|
||||||
|
|||||||
Reference in New Issue
Block a user