using System; using System.Reactive.Disposables; using Avalonia.Platform; namespace Avalonia.Threading { /// /// A timer that uses a to fire at a specified interval. /// public class DispatcherTimer { private IDisposable? _timer; private readonly DispatcherPriority _priority; private TimeSpan _interval; /// /// Initializes a new instance of the class. /// public DispatcherTimer() : this(DispatcherPriority.Background) { } /// /// Initializes a new instance of the class. /// /// The priority to use. public DispatcherTimer(DispatcherPriority priority) { _priority = priority; } /// /// Initializes a new instance of the class. /// /// The interval at which to tick. /// The priority to use. /// The event to call when the timer ticks. public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback) : this(priority) { _priority = priority; Interval = interval; Tick += callback; } /// /// Finalizes an instance of the class. /// ~DispatcherTimer() { if (_timer != null) { Stop(); } } /// /// Raised when the timer ticks. /// public event EventHandler? Tick; /// /// Gets or sets the interval at which the timer ticks. /// public TimeSpan Interval { get { return _interval; } set { bool enabled = IsEnabled; Stop(); _interval = value; IsEnabled = enabled; } } /// /// Gets or sets a value indicating whether the timer is running. /// public bool IsEnabled { get { return _timer != null; } set { if (IsEnabled != value) { if (value) { Start(); } else { Stop(); } } } } /// /// Gets or sets user-defined data associated with the timer. /// public object? Tag { get; set; } /// /// Starts a new timer. /// /// /// The method to call on timer tick. If the method returns false, the timer will stop. /// /// The interval at which to tick. /// The priority to use. /// An used to cancel the timer. public static IDisposable Run(Func action, TimeSpan interval, DispatcherPriority priority = default) { var timer = new DispatcherTimer(priority) { Interval = interval }; timer.Tick += (s, e) => { if (!action()) { timer.Stop(); } }; timer.Start(); return Disposable.Create(() => timer.Stop()); } /// /// Runs a method once, after the specified interval. /// /// /// The method to call after the interval has elapsed. /// /// The interval after which to call the method. /// The priority to use. /// An used to cancel the timer. public static IDisposable RunOnce( Action action, TimeSpan interval, DispatcherPriority priority = default) { interval = (interval != TimeSpan.Zero) ? interval : TimeSpan.FromTicks(1); var timer = new DispatcherTimer(priority) { Interval = interval }; timer.Tick += (s, e) => { action(); timer.Stop(); }; timer.Start(); return Disposable.Create(() => timer.Stop()); } /// /// Starts the timer. /// public void Start() { if (!IsEnabled) { var threading = AvaloniaLocator.Current.GetRequiredService(); _timer = threading.StartTimer(_priority, Interval, InternalTick); } } /// /// Stops the timer. /// public void Stop() { if (IsEnabled) { _timer!.Dispose(); _timer = null; } } /// /// Raises the event on the dispatcher thread. /// private void InternalTick() { Dispatcher.UIThread.EnsurePriority(_priority); Tick?.Invoke(this, EventArgs.Empty); } } }