|
|
|
@ -4,6 +4,7 @@ using System.Diagnostics; |
|
|
|
using Avalonia.Animation; |
|
|
|
using Avalonia.Reactive; |
|
|
|
using Avalonia.Threading; |
|
|
|
using Avalonia.Utilities; |
|
|
|
|
|
|
|
namespace Avalonia.Media; |
|
|
|
|
|
|
|
@ -17,8 +18,12 @@ internal partial class MediaContext |
|
|
|
{ |
|
|
|
private readonly MediaContext _parent; |
|
|
|
private List<IObserver<TimeSpan>> _observers = new(); |
|
|
|
public bool HasNewSubscriptions { get; set; } |
|
|
|
public bool HasSubscriptions => _observers.Count > 0; |
|
|
|
private List<IObserver<TimeSpan>> _newObservers = new(); |
|
|
|
private Queue<Action<TimeSpan>> _queuedAnimationFrames = new(); |
|
|
|
private Queue<Action<TimeSpan>> _queuedAnimationFramesNext = new(); |
|
|
|
private TimeSpan _currentAnimationTimestamp; |
|
|
|
public bool HasNewSubscriptions => _newObservers.Count > 0; |
|
|
|
public bool HasSubscriptions => _observers.Count > 0 || _queuedAnimationFrames.Count > 0; |
|
|
|
|
|
|
|
public MediaContextClock(MediaContext parent) |
|
|
|
{ |
|
|
|
@ -29,19 +34,41 @@ internal partial class MediaContext |
|
|
|
{ |
|
|
|
_parent.ScheduleRender(false); |
|
|
|
Dispatcher.UIThread.VerifyAccess(); |
|
|
|
HasNewSubscriptions = true; |
|
|
|
_observers.Add(observer); |
|
|
|
_newObservers.Add(observer); |
|
|
|
return Disposable.Create(() => |
|
|
|
{ |
|
|
|
Dispatcher.UIThread.VerifyAccess(); |
|
|
|
_observers.Remove(observer); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
public void RequestAnimationFrame(Action<TimeSpan> action) |
|
|
|
{ |
|
|
|
_parent.ScheduleRender(false); |
|
|
|
_queuedAnimationFrames.Enqueue(action); |
|
|
|
} |
|
|
|
|
|
|
|
public void Pulse(TimeSpan now) |
|
|
|
{ |
|
|
|
_newObservers.Clear(); |
|
|
|
_currentAnimationTimestamp = now; |
|
|
|
|
|
|
|
// We are swapping the queues before enumeration
|
|
|
|
(_queuedAnimationFrames, _queuedAnimationFramesNext) = (_queuedAnimationFramesNext, _queuedAnimationFrames); |
|
|
|
var animationFrames = _queuedAnimationFramesNext; |
|
|
|
while (animationFrames.TryDequeue(out var callback)) |
|
|
|
callback(now); |
|
|
|
|
|
|
|
foreach (var observer in _observers.ToArray()) |
|
|
|
observer.OnNext(now); |
|
|
|
observer.OnNext(_currentAnimationTimestamp); |
|
|
|
} |
|
|
|
|
|
|
|
public void PulseNewSubscriptions() |
|
|
|
{ |
|
|
|
foreach (var observer in _newObservers.ToArray()) |
|
|
|
observer.OnNext(_currentAnimationTimestamp); |
|
|
|
_newObservers.Clear(); |
|
|
|
} |
|
|
|
|
|
|
|
public PlayState PlayState |
|
|
|
@ -50,4 +77,6 @@ internal partial class MediaContext |
|
|
|
set => throw new InvalidOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void RequestAnimationFrame(Action<TimeSpan> action) => _clock.RequestAnimationFrame(action); |
|
|
|
} |