using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace Uigenx.Animation
{
///
/// Applies one animatable style property. Parsing happens once when a clip is prepared;
/// runs every tick and must not allocate.
///
public interface IUgxStyleSetter
{
/// Canonical USS property name, for example `background-color`.
string Property { get; }
bool TryParse(string text, out UgxStyleValue value);
bool TryRead(VisualElement element, out UgxStyleValue value);
void Apply(VisualElement element, in UgxStyleValue value);
}
/// Property name to setter table. Accepts USS names and their C# spellings.
public static class UgxStyleSetters
{
static readonly Dictionary Table = Build();
public static bool TryGet(string property, out IUgxStyleSetter setter)
{
setter = null;
if (string.IsNullOrEmpty(property)) return false;
if (property.StartsWith("style.", System.StringComparison.Ordinal)) property = property.Substring(6);
return Table.TryGetValue(property, out setter);
}
public static IEnumerable Properties => Table.Keys;
static Dictionary Build()
{
var table = new Dictionary(32, System.StringComparer.Ordinal);
Register(table, new OpacitySetter(), "opacity");
Register(table, new TranslateSetter(), "translate");
Register(table, new ScaleSetter(), "scale");
Register(table, new RotateSetter(), "rotate");
Register(table, new LengthSetter("width"), "width");
Register(table, new LengthSetter("height"), "height");
Register(table, new LengthSetter("left"), "left");
Register(table, new LengthSetter("top"), "top");
Register(table, new ColorSetter("background-color"), "background-color", "backgroundColor");
Register(table, new ColorSetter("color"), "color");
Register(table, new ColorSetter("-unity-background-image-tint-color"),
"-unity-background-image-tint-color", "unityBackgroundImageTintColor");
return table;
}
static void Register(Dictionary table, IUgxStyleSetter setter, params string[] names)
{
foreach (var name in names) table[name] = setter;
}
sealed class OpacitySetter : IUgxStyleSetter
{
public string Property => "opacity";
public bool TryParse(string text, out UgxStyleValue value)
{
var ok = UgxValueParser.TryFloat(text, out var f);
value = UgxStyleValue.Number(f);
return ok;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
value = UgxStyleValue.Number(element.resolvedStyle.opacity);
return true;
}
public void Apply(VisualElement element, in UgxStyleValue value) => element.style.opacity = value.A;
}
sealed class LengthSetter : IUgxStyleSetter
{
readonly string _property;
internal LengthSetter(string property) => _property = property;
public string Property => _property;
public bool TryParse(string text, out UgxStyleValue value)
{
var ok = UgxValueParser.TryLength(text, out var v, out var unit);
value = new UgxStyleValue(v, unitA: unit);
return ok;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
var resolved = element.resolvedStyle;
float v;
switch (_property)
{
case "width": v = resolved.width; break;
case "height": v = resolved.height; break;
case "left": v = resolved.left; break;
default: v = resolved.top; break;
}
value = new UgxStyleValue(v);
return !float.IsNaN(v);
}
public void Apply(VisualElement element, in UgxStyleValue value)
{
var length = value.LengthA;
switch (_property)
{
case "width": element.style.width = length; break;
case "height": element.style.height = length; break;
case "left": element.style.left = length; break;
default: element.style.top = length; break;
}
}
}
sealed class ColorSetter : IUgxStyleSetter
{
readonly string _property;
internal ColorSetter(string property) => _property = property;
public string Property => _property;
public bool TryParse(string text, out UgxStyleValue value)
{
var ok = UgxValueParser.TryColor(text, out var c);
value = UgxStyleValue.FromColor(c);
return ok;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
var resolved = element.resolvedStyle;
Color c;
switch (_property)
{
case "background-color": c = resolved.backgroundColor; break;
case "color": c = resolved.color; break;
default: c = resolved.unityBackgroundImageTintColor; break;
}
value = UgxStyleValue.FromColor(c);
return true;
}
public void Apply(VisualElement element, in UgxStyleValue value)
{
var c = value.ToColor();
switch (_property)
{
case "background-color": element.style.backgroundColor = c; break;
case "color": element.style.color = c; break;
default: element.style.unityBackgroundImageTintColor = c; break;
}
}
}
sealed class TranslateSetter : IUgxStyleSetter
{
public string Property => "translate";
public bool TryParse(string text, out UgxStyleValue value)
{
var ok = UgxValueParser.TryPair(text, out var x, out var ux, out var y, out var uy);
value = new UgxStyleValue(x, y, unitA: ux, unitB: uy);
return ok;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
var t = element.resolvedStyle.translate;
value = new UgxStyleValue(t.x, t.y);
return true;
}
public void Apply(VisualElement element, in UgxStyleValue value)
=> element.style.translate = new Translate(value.LengthA, value.LengthB);
}
sealed class ScaleSetter : IUgxStyleSetter
{
public string Property => "scale";
public bool TryParse(string text, out UgxStyleValue value)
{
var s = text == null ? string.Empty : text.Trim();
int split = s.IndexOf(' ');
if (split < 0)
{
var single = UgxValueParser.TryFloat(s, out var v);
value = new UgxStyleValue(v, v);
return single;
}
bool okX = UgxValueParser.TryFloat(s.Substring(0, split), out var x);
bool okY = UgxValueParser.TryFloat(s.Substring(split + 1), out var y);
value = new UgxStyleValue(x, y);
return okX && okY;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
var s = element.resolvedStyle.scale.value;
value = new UgxStyleValue(s.x, s.y);
return true;
}
public void Apply(VisualElement element, in UgxStyleValue value)
=> element.style.scale = new Scale(new Vector2(value.A, value.B));
}
sealed class RotateSetter : IUgxStyleSetter
{
public string Property => "rotate";
public bool TryParse(string text, out UgxStyleValue value)
{
var ok = UgxValueParser.TryAngle(text, out var v, out var unit);
value = new UgxStyleValue(v, unitA: unit);
return ok;
}
public bool TryRead(VisualElement element, out UgxStyleValue value)
{
var angle = element.resolvedStyle.rotate.angle;
value = new UgxStyleValue(angle.value, unitA: (byte)angle.unit);
return true;
}
public void Apply(VisualElement element, in UgxStyleValue value)
=> element.style.rotate = new Rotate(value.AngleA);
}
}
}