using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Avalonia.Logging; using Avalonia.Media; #nullable enable namespace Avalonia.Animation.Animators { /// /// Animator that handles all animations on properties /// with as their type and /// redirect them to the properly registered /// animators in this class. /// public class BaseBrushAnimator : Animator { private static readonly List<(Func Match, Type AnimatorType)> _brushAnimators = new List<(Func Match, Type AnimatorType)>(); /// /// Register an that handles a specific /// 's descendant value type. /// /// /// The condition to which the /// is to be activated and used. /// /// /// The type of the animator to instantiate. /// public static void RegisterBrushAnimator(Func condition) where TAnimator : IAnimator, new() { _brushAnimators.Insert(0, (condition, typeof(TAnimator))); } /// public override IDisposable? Apply(Animation animation, Animatable control, IClock? clock, IObservable match, Action? onComplete) { if (TryCreateCustomRegisteredAnimator(out var animator) || TryCreateGradientAnimator(out animator) || TryCreateSolidColorBrushAnimator(out animator)) { return animator.Apply(animation, control, clock, match, onComplete); } Logger.TryGet(LogEventLevel.Error, LogArea.Animations)?.Log( this, "The animation's keyframe value types set is not supported."); return base.Apply(animation, control, clock, match, onComplete); } /// /// Fallback implementation of animation. /// public override IBrush? Interpolate(double progress, IBrush? oldValue, IBrush? newValue) => progress >= 0.5 ? newValue : oldValue; private bool TryCreateGradientAnimator([NotNullWhen(true)] out IAnimator? animator) { IGradientBrush? firstGradient = null; foreach (var keyframe in this) { if (keyframe.Value is IGradientBrush gradientBrush) { firstGradient = gradientBrush; break; } } if (firstGradient is null) { animator = null; return false; } var gradientAnimator = new GradientBrushAnimator(); gradientAnimator.Property = Property; foreach (var keyframe in this) { if (keyframe.Value is ISolidColorBrush solidColorBrush) { gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) { Value = GradientBrushAnimator.ConvertSolidColorBrushToGradient(firstGradient, solidColorBrush) }); } else if (keyframe.Value is IGradientBrush) { gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline) { Value = keyframe.Value }); } else { animator = null; return false; } } animator = gradientAnimator; return true; } private bool TryCreateSolidColorBrushAnimator([NotNullWhen(true)] out IAnimator? animator) { var solidColorBrushAnimator = new ISolidColorBrushAnimator(); solidColorBrushAnimator.Property = Property; foreach (var keyframe in this) { if (keyframe.Value is ISolidColorBrush) { solidColorBrushAnimator.Add(new AnimatorKeyFrame(typeof(ISolidColorBrushAnimator), keyframe.Cue, keyframe.KeySpline) { Value = keyframe.Value }); } else { animator = null; return false; } } animator = solidColorBrushAnimator; return true; } private bool TryCreateCustomRegisteredAnimator([NotNullWhen(true)] out IAnimator? animator) { if (_brushAnimators.Count > 0 && this[0].Value?.GetType() is Type firstKeyType) { foreach (var (match, animatorType) in _brushAnimators) { if (!match(firstKeyType)) continue; animator = (IAnimator?)Activator.CreateInstance(animatorType); if (animator != null) { animator.Property = Property; foreach (var keyframe in this) { animator.Add(new AnimatorKeyFrame(animatorType, keyframe.Cue, keyframe.KeySpline) { Value = keyframe.Value }); } return true; } } } animator = null; return false; } } }