Files
Speckle.Material.Avalonia/Material.Styles/Controls/Rotator.cs
T
2022-07-05 10:21:35 +09:00

116 lines
3.5 KiB
C#

using System;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Rendering;
// ReSharper disable ConvertToLambdaExpression
namespace Material.Styles.Controls
{
public class Rotator : Panel
{
private static IRenderLoop? _loopInstance;
private static IRenderLoop LoopInstance => _loopInstance ??= new RenderLoop();
// Minimum speed
// Rotator will stop if speed less than this constant value.
// It is able to change in code-behind if this value is not satisfied.
private static double _minimumSpeed = 0.0025;
// ReSharper disable once MemberCanBePrivate.Global
public static double MinimumSpeed
{
get => _minimumSpeed;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value),
"MinimumSpeed should not less than zero. You can set it as zero, if you wish your rotator keep running.");
_minimumSpeed = value;
}
}
private bool _running;
private double _speed = 0.4;
private double _rotateDegree;
private readonly RenderLoopClock _loopTask;
private TimeSpan _prev;
public Rotator()
{
_loopInstance ??= AvaloniaLocator.Current.GetService<IRenderLoop>() ?? LoopInstance;
// Prepare render loop task for use.
_loopTask = new RenderLoopClock();
_loopTask.Subscribe(OnLoopUpdate);
}
public double Speed
{
get => _speed;
set => SetAndRaise(SpeedProperty, ref _speed, value);
}
public static readonly DirectProperty<Rotator, double> SpeedProperty =
AvaloniaProperty.RegisterDirect(nameof(Speed),
delegate(Rotator rotator) { return rotator._speed; },
delegate(Rotator rotator, double v)
{
if (rotator.IsEffectivelyVisible == false || rotator.IsEffectivelyEnabled == false)
return;
rotator._speed = v;
OnSpeedChanged(rotator, v);
});
// Loop dispatcher / simple loop controller
private void OnLoopUpdate(TimeSpan renderTime)
{
if (IsEffectivelyVisible == false || IsEffectivelyEnabled == false)
return;
var delta = renderTime - _prev;
_rotateDegree += _speed * delta.TotalMilliseconds;
_prev = renderTime;
while (_rotateDegree > 360)
_rotateDegree -= 360;
RenderTransform = new RotateTransform(_rotateDegree);
}
private static void OnSpeedChanged(Rotator rotator, double d)
{
// We should stop rotator if speed is lower than minimum speed
if (Math.Abs(d) < _minimumSpeed)
{
// Stop render loop to avoid leak of performances.
if (!rotator._running)
return;
// Reset statements
rotator._running = false;
rotator._rotateDegree = 0;
// Detach loop task from RenderLoop
LoopInstance.Remove(rotator._loopTask);
return;
}
if (rotator._running)
return;
// Attach loop task to RenderLoop if not running.
rotator._running = true;
LoopInstance.Add(rotator._loopTask);
}
}
}