Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/fullFontTestBackend' int…
Browse files Browse the repository at this point in the history
…o feature/fullFontTestBackend
  • Loading branch information
Gillibald committed Jan 11, 2022
2 parents 2497aeb + 6250bf6 commit 08b72d2
Show file tree
Hide file tree
Showing 32 changed files with 229 additions and 150 deletions.
1 change: 1 addition & 0 deletions samples/ControlCatalog/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:vm="using:ControlCatalog.ViewModels"
x:DataType="vm:ApplicationViewModel"
x:CompileBindings="True"
Name="Avalonia ControlCatalog"
x:Class="ControlCatalog.App">
<Application.Styles>
<Style Selector="TextBlock.h1, TextBlock.h2, TextBlock.h3">
Expand Down
16 changes: 8 additions & 8 deletions src/Avalonia.Animation/Animatable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ _transitionState is object &&
state.Instance?.Dispose();
state.Instance = transition.Apply(
this,
Clock ?? AvaloniaLocator.Current.GetService<IGlobalClock>(),
Clock ?? AvaloniaLocator.Current.GetRequiredService<IGlobalClock>(),
oldValue,
newValue);
return;
Expand All @@ -169,7 +169,7 @@ _transitionState is object &&
base.OnPropertyChangedCore(change);
}

private void TransitionsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
private void TransitionsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (!_transitionsEnabled)
{
Expand All @@ -179,14 +179,14 @@ private void TransitionsCollectionChanged(object sender, NotifyCollectionChanged
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
AddTransitions(e.NewItems);
AddTransitions(e.NewItems!);
break;
case NotifyCollectionChangedAction.Remove:
RemoveTransitions(e.OldItems);
RemoveTransitions(e.OldItems!);
break;
case NotifyCollectionChangedAction.Replace:
RemoveTransitions(e.OldItems);
AddTransitions(e.NewItems);
RemoveTransitions(e.OldItems!);
AddTransitions(e.NewItems!);
break;
case NotifyCollectionChangedAction.Reset:
throw new NotSupportedException("Transitions collection cannot be reset.");
Expand All @@ -204,7 +204,7 @@ private void AddTransitions(IList items)

for (var i = 0; i < items.Count; ++i)
{
var t = (ITransition)items[i];
var t = (ITransition)items[i]!;

_transitionState.Add(t, new TransitionState
{
Expand All @@ -222,7 +222,7 @@ private void RemoveTransitions(IList items)

for (var i = 0; i < items.Count; ++i)
{
var t = (ITransition)items[i];
var t = (ITransition)items[i]!;

if (_transitionState.TryGetValue(t, out var state))
{
Expand Down
46 changes: 31 additions & 15 deletions src/Avalonia.Animation/Animation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public string RepeatCount
/// </summary>
/// <param name="setter">The animation setter.</param>
/// <returns>The property animator type.</returns>
public static Type GetAnimator(IAnimationSetter setter)
public static Type? GetAnimator(IAnimationSetter setter)
{
if (s_animators.TryGetValue(setter, out var type))
{
Expand Down Expand Up @@ -254,7 +254,7 @@ public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> cond
Animators.Insert(0, (condition, typeof(TAnimator)));
}

private static Type GetAnimatorType(AvaloniaProperty property)
private static Type? GetAnimatorType(AvaloniaProperty property)
{
foreach (var (condition, type) in Animators)
{
Expand All @@ -276,6 +276,11 @@ private static Type GetAnimatorType(AvaloniaProperty property)
{
foreach (var setter in keyframe.Setters)
{
if (setter.Property is null)
{
throw new InvalidOperationException("No Setter property assigned.");
}

var handler = Animation.GetAnimator(setter) ?? GetAnimatorType(setter.Property);

if (handler == null)
Expand Down Expand Up @@ -305,7 +310,7 @@ private static Type GetAnimatorType(AvaloniaProperty property)

foreach (var (handlerType, property) in handlerList)
{
var newInstance = (IAnimator)Activator.CreateInstance(handlerType);
var newInstance = (IAnimator)Activator.CreateInstance(handlerType)!;
newInstance.Property = property;
newAnimatorInstances.Add(newInstance);
}
Expand All @@ -321,58 +326,69 @@ private static Type GetAnimatorType(AvaloniaProperty property)
}

/// <inheritdoc/>
public IDisposable Apply(Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
public IDisposable Apply(Animatable control, IClock? clock, IObservable<bool> match, Action? onComplete)
{
var (animators, subscriptions) = InterpretKeyframes(control);
if (animators.Count == 1)
{
subscriptions.Add(animators[0].Apply(this, control, clock, match, onComplete));
var subscription = animators[0].Apply(this, control, clock, match, onComplete);

if (subscription is not null)
{
subscriptions.Add(subscription);
}
}
else
{
var completionTasks = onComplete != null ? new List<Task>() : null;
foreach (IAnimator animator in animators)
{
Action animatorOnComplete = null;
Action? animatorOnComplete = null;
if (onComplete != null)
{
var tcs = new TaskCompletionSource<object>();
var tcs = new TaskCompletionSource<object?>();
animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task);
completionTasks!.Add(tcs.Task);
}

var subscription = animator.Apply(this, control, clock, match, animatorOnComplete);

if (subscription is not null)
{
subscriptions.Add(subscription);
}
subscriptions.Add(animator.Apply(this, control, clock, match, animatorOnComplete));
}

if (onComplete != null)
{
Task.WhenAll(completionTasks).ContinueWith(
(_, state) => ((Action)state).Invoke(),
Task.WhenAll(completionTasks!).ContinueWith(
(_, state) => ((Action)state!).Invoke(),
onComplete);
}
}
return new CompositeDisposable(subscriptions);
}

/// <inheritdoc/>
public Task RunAsync(Animatable control, IClock clock = null)
public Task RunAsync(Animatable control, IClock? clock = null)
{
return RunAsync(control, clock, default);
}

/// <inheritdoc/>
public Task RunAsync(Animatable control, IClock clock = null, CancellationToken cancellationToken = default)
public Task RunAsync(Animatable control, IClock? clock = null, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
return Task.CompletedTask;
}

var run = new TaskCompletionSource<object>();
var run = new TaskCompletionSource<object?>();

if (this.IterationCount == IterationCount.Infinite)
run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));

IDisposable subscriptions = null, cancellation = null;
IDisposable? subscriptions = null, cancellation = null;
subscriptions = this.Apply(control, clock, Observable.Return(true), () =>
{
run.TrySetResult(null);
Expand Down
33 changes: 19 additions & 14 deletions src/Avalonia.Animation/AnimationInstance`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,25 @@ internal class AnimationInstance<T> : SingleSubscriberObservableBase<T>
private TimeSpan _initialDelay;
private TimeSpan _iterationDelay;
private TimeSpan _duration;
private Easings.Easing _easeFunc;
private Action _onCompleteAction;
private Easings.Easing? _easeFunc;
private Action? _onCompleteAction;
private Func<double, T, T> _interpolator;
private IDisposable _timerSub;
private IDisposable? _timerSub;
private readonly IClock _baseClock;
private IClock _clock;
private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedDelegate;
private IClock? _clock;
private EventHandler<AvaloniaPropertyChangedEventArgs>? _propertyChangedDelegate;

public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action OnComplete, Func<double, T, T> Interpolator)
public AnimationInstance(Animation animation, Animatable control, Animator<T> animator, IClock baseClock, Action? OnComplete, Func<double, T, T> Interpolator)
{
_animator = animator;
_animation = animation;
_targetControl = control;
_onCompleteAction = OnComplete;
_interpolator = Interpolator;
_baseClock = baseClock;
_lastInterpValue = default!;
_firstKFValue = default!;
_neutralValue = default!;
FetchProperties();
}

Expand Down Expand Up @@ -82,7 +85,7 @@ protected override void Unsubscribed()

_targetControl.PropertyChanged -= _propertyChangedDelegate;
_timerSub?.Dispose();
_clock.PlayState = PlayState.Stop;
_clock!.PlayState = PlayState.Stop;
}

protected override void Subscribed()
Expand All @@ -108,6 +111,8 @@ public void Step(TimeSpan frameTick)

private void ApplyFinalFill()
{
if (_animator.Property is null)
throw new InvalidOperationException("Animator has no property specified.");
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both)
_targetControl.SetValue(_animator.Property, _lastInterpValue, BindingPriority.LocalValue);
}
Expand All @@ -130,12 +135,12 @@ private void DoDelay()

private void DoPlayStates()
{
if (_clock.PlayState == PlayState.Stop || _baseClock.PlayState == PlayState.Stop)
if (_clock!.PlayState == PlayState.Stop || _baseClock.PlayState == PlayState.Stop)
DoComplete();

if (!_gotFirstKFValue)
{
_firstKFValue = (T)_animator.First().Value;
_firstKFValue = (T)_animator.First().Value!;
_gotFirstKFValue = true;
}
}
Expand Down Expand Up @@ -169,7 +174,7 @@ private void InternalStep(TimeSpan time)
// and snap the last iteration value to exact values.
if ((_currentIteration + 1) > _iterationCount)
{
var easedTime = _easeFunc.Ease(_playbackReversed ? 0.0 : 1.0);
var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
DoComplete();
}
Expand Down Expand Up @@ -203,7 +208,7 @@ private void InternalStep(TimeSpan time)
normalizedTime = 1 - normalizedTime;

// Ease and interpolate
var easedTime = _easeFunc.Ease(normalizedTime);
var easedTime = _easeFunc!.Ease(normalizedTime);
_lastInterpValue = _interpolator(easedTime, _neutralValue);

PublishNext(_lastInterpValue);
Expand All @@ -223,14 +228,14 @@ private void InternalStep(TimeSpan time)

private void UpdateNeutralValue()
{
var property = _animator.Property;
var property = _animator.Property ?? throw new InvalidOperationException("Animator has no property specified.");
var baseValue = _targetControl.GetBaseValue(property, BindingPriority.LocalValue);

_neutralValue = baseValue != AvaloniaProperty.UnsetValue ?
(T)baseValue : (T)_targetControl.GetValue(property);
(T)baseValue! : (T)_targetControl.GetValue(property)!;
}

private void ControlPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
private void ControlPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == _animator.Property && e.Priority > BindingPriority.Animation)
{
Expand Down
20 changes: 10 additions & 10 deletions src/Avalonia.Animation/AnimatorKeyFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,37 @@ namespace Avalonia.Animation
/// </summary>
public class AnimatorKeyFrame : AvaloniaObject
{
public static readonly DirectProperty<AnimatorKeyFrame, object> ValueProperty =
AvaloniaProperty.RegisterDirect<AnimatorKeyFrame, object>(nameof(Value), k => k.Value, (k, v) => k.Value = v);
public static readonly DirectProperty<AnimatorKeyFrame, object?> ValueProperty =
AvaloniaProperty.RegisterDirect<AnimatorKeyFrame, object?>(nameof(Value), k => k.Value, (k, v) => k.Value = v);

public AnimatorKeyFrame()
{

}

public AnimatorKeyFrame(Type animatorType, Cue cue)
public AnimatorKeyFrame(Type? animatorType, Cue cue)
{
AnimatorType = animatorType;
Cue = cue;
KeySpline = null;
}

public AnimatorKeyFrame(Type animatorType, Cue cue, KeySpline keySpline)
public AnimatorKeyFrame(Type? animatorType, Cue cue, KeySpline? keySpline)
{
AnimatorType = animatorType;
Cue = cue;
KeySpline = keySpline;
}

internal bool isNeutral;
public Type AnimatorType { get; }
public Type? AnimatorType { get; }
public Cue Cue { get; }
public KeySpline KeySpline { get; }
public AvaloniaProperty Property { get; private set; }
public KeySpline? KeySpline { get; }
public AvaloniaProperty? Property { get; private set; }

private object _value;
private object? _value;

public object Value
public object? Value
{
get => _value;
set => SetAndRaise(ValueProperty, ref _value, value);
Expand Down Expand Up @@ -80,7 +80,7 @@ public T GetTypedValue<T>()
throw new InvalidCastException($"KeyFrame value doesnt match property type.");
}

return (T)typeConv.ConvertTo(Value, typeof(T));
return (T)typeConv.ConvertTo(Value, typeof(T))!;
}
}
}
9 changes: 6 additions & 3 deletions src/Avalonia.Animation/Animators/Animator`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public abstract class Animator<T> : AvaloniaList<AnimatorKeyFrame>, IAnimator
/// <summary>
/// Gets or sets the target property for the keyframe.
/// </summary>
public AvaloniaProperty Property { get; set; }
public AvaloniaProperty? Property { get; set; }

public Animator()
{
Expand All @@ -33,7 +33,7 @@ public Animator()
}

/// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IClock clock, IObservable<bool> match, Action onComplete)
public virtual IDisposable? Apply(Animation animation, Animatable control, IClock? clock, IObservable<bool> match, Action? onComplete)
{
if (!_isVerifiedAndConverted)
VerifyConvertKeyFrames();
Expand Down Expand Up @@ -106,13 +106,16 @@ private int FindClosestBeforeKeyFrame(double time)

public virtual IDisposable BindAnimation(Animatable control, IObservable<T> instance)
{
if (Property is null)
throw new InvalidOperationException("Animator has no property specified.");

return control.Bind((AvaloniaProperty<T>)Property, instance, BindingPriority.Animation);
}

/// <summary>
/// Runs the KeyFrames Animation.
/// </summary>
internal IDisposable Run(Animation animation, Animatable control, IClock clock, Action onComplete)
internal IDisposable Run(Animation animation, Animatable control, IClock? clock, Action? onComplete)
{
var instance = new AnimationInstance<T>(
animation,
Expand Down
4 changes: 4 additions & 0 deletions src/Avalonia.Animation/Avalonia.Animation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<PropertyGroup>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" />
</Project>
2 changes: 1 addition & 1 deletion src/Avalonia.Animation/Clock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Avalonia.Animation
{
public class Clock : ClockBase
{
public static IClock GlobalClock => AvaloniaLocator.Current.GetService<IGlobalClock>();
public static IClock GlobalClock => AvaloniaLocator.Current.GetRequiredService<IGlobalClock>();

private readonly IDisposable _parentSubscription;

Expand Down
Loading

0 comments on commit 08b72d2

Please sign in to comment.