| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 | 
							- using Avalonia;
 
- using Avalonia.Controls;
 
- using Avalonia.Media;
 
- using Avalonia.Threading;
 
- using System.Windows.Input;
 
- using InABox.Core;
 
- namespace InABox.Avalonia.Components;
 
- public class CircularCountdownTimer : Control
 
- {
 
-     private readonly DispatcherTimer _timer;
 
-     private DateTime? _startTime;
 
-     
 
-     public static readonly StyledProperty<IImage?> ImageProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, IImage?>(nameof(Image));
 
-     public IImage? Image
 
-     {
 
-         get => GetValue(ImageProperty);
 
-         set => SetValue(ImageProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<bool> IsActiveProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, bool>(nameof(IsActive));
 
-     public bool IsActive
 
-     {
 
-         get => GetValue(IsActiveProperty);
 
-         set => SetValue(IsActiveProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<ICommand?> StartedProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, ICommand?>(nameof(Started));
 
-     public ICommand? Started
 
-     {
 
-         get => GetValue(StartedProperty);
 
-         set => SetValue(StartedProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<ICommand?> StoppedProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, ICommand?>(nameof(Stopped));
 
-     public ICommand? Stopped
 
-     {
 
-         get => GetValue(StoppedProperty);
 
-         set => SetValue(StoppedProperty, value);
 
-     }
 
-     
 
-         
 
-     public static readonly StyledProperty<IBrush> BackgroundProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(Background), Brushes.Transparent);
 
-     public IBrush Background
 
-     {
 
-         get => GetValue(BackgroundProperty);
 
-         set => SetValue(BackgroundProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<double> StrokeThicknessProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, double>(nameof(StrokeThickness), 10.0);
 
-     public double StrokeThickness
 
-     {
 
-         get => GetValue(StrokeThicknessProperty);
 
-         set => SetValue(StrokeThicknessProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<IBrush> ProgressBackgroundProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(ProgressBackground), Brushes.Gray);
 
-     public IBrush ProgressBackground
 
-     {
 
-         get => GetValue(ProgressBackgroundProperty);
 
-         set => SetValue(ProgressBackgroundProperty, value);
 
-     }
 
-     
 
-     public static readonly StyledProperty<IBrush> ProgressForegroundProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, IBrush>(nameof(ProgressForeground), Brushes.WhiteSmoke);
 
-     public IBrush ProgressForeground
 
-     {
 
-         get => GetValue(ProgressForegroundProperty);
 
-         set => SetValue(ProgressForegroundProperty, value);
 
-     }
 
-     
 
-     protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
 
-     {
 
-         base.OnPropertyChanged(change);
 
-         if (change.Property == IsActiveProperty)
 
-         {
 
-             if (Equals(change.NewValue,true))
 
-                 Start();
 
-             else
 
-                 Stop();
 
-         }
 
-     }
 
-     public static readonly StyledProperty<double> DurationProperty =
 
-         AvaloniaProperty.Register<CircularCountdownTimer, double>(nameof(Duration), 15.0);
 
-     public double Duration
 
-     {
 
-         get => GetValue(DurationProperty);
 
-         set
 
-         {
 
-             SetValue(DurationProperty, value);
 
-             InvalidateVisual();
 
-         }
 
-     }
 
-     
 
-     public CircularCountdownTimer()
 
-     {
 
-         _timer = new DispatcherTimer(DispatcherPriority.MaxValue)
 
-         {
 
-             Interval = TimeSpan.FromMilliseconds(200),
 
-         };
 
-         _timer.Tick += TimerTick;
 
-     }
 
-     private void Start()
 
-     {
 
-         _startTime = DateTime.Now;
 
-         _timer.Start();
 
-         Started?.Execute(DataContext);
 
-         InvalidateVisual();
 
-     }
 
-     
 
-     private void Stop()
 
-     {
 
-         _timer.Stop();
 
-         _startTime = null;
 
-         Stopped?.Execute(DataContext);
 
-         InvalidateVisual();
 
-     }
 
-     
 
-     private TimeSpan RemainingTime()
 
-     {
 
-         if (!_startTime.HasValue)
 
-             return TimeSpan.Zero;
 
-         
 
-         var _nowTime = DateTime.Now;
 
-         var _endTime = _startTime.Value.AddSeconds(Duration);
 
-         return _endTime > _nowTime 
 
-             ? _endTime - _nowTime 
 
-             : TimeSpan.Zero;
 
-     }
 
-     
 
-     private void TimerTick(object? sender, EventArgs e)
 
-     {
 
-         if (RemainingTime() <= TimeSpan.Zero)
 
-             IsActive = false;
 
-         else
 
-             InvalidateVisual();
 
-     }
 
-     
 
-     public override void Render(DrawingContext context)
 
-     {
 
-         var boundsRect = new Rect(Bounds.Left+Margin.Left, Bounds.Top+Margin.Top, Bounds.Width-(Margin.Left+Margin.Right), Bounds.Height-(Margin.Top+Margin.Bottom));
 
-         
 
-         double centerX = boundsRect.Width / 2;
 
-         double centerY = boundsRect.Height / 2;
 
-         double radius = Math.Min(centerX, centerY) - StrokeThickness;
 
-         var backgroundPen = new Pen(ProgressBackground, StrokeThickness);
 
-         var progressPen = new Pen(ProgressForeground, StrokeThickness)
 
-         {
 
-             LineCap = PenLineCap.Flat // Smooth arc edges
 
-         };
 
-         context.FillRectangle(Background, boundsRect);
 
-         // Draw background circle
 
-         context.DrawEllipse(null, backgroundPen, new Point(centerX, centerY), radius, radius);
 
-         if (Image != null)
 
-         {
 
-             var boxwidth = Math.Sqrt(2) * radius;
 
-             Rect rect = new Rect(centerX - (boxwidth/2), centerY - (boxwidth/2), boxwidth, boxwidth);
 
-             context.DrawImage(Image, rect);
 
-         }
 
-         double _remainingTime = RemainingTime().TotalSeconds;
 
-         if (_remainingTime.IsEffectivelyGreaterThan(0.0))
 
-         {
 
-             // Calculate the sweep angle
 
-             double sweepAngle = (_remainingTime / Duration) * 360;
 
-             double startAngle = -90;
 
-             double endAngle = startAngle + sweepAngle;
 
-             // Convert angles to radians
 
-             double startRad = Math.PI * startAngle / 180.0;
 
-             double endRad = Math.PI * endAngle / 180.0;
 
-             // Calculate points on the circle
 
-             var startPoint = new Point(
 
-                 centerX + radius * Math.Cos(startRad),
 
-                 centerY + radius * Math.Sin(startRad)
 
-             );
 
-             var endPoint = new Point(
 
-                 centerX + radius * Math.Cos(endRad),
 
-                 centerY + radius * Math.Sin(endRad)
 
-             );
 
-             // Create an arc geometry
 
-             var geometry = new StreamGeometry();
 
-             using (var ctx = geometry.Open())
 
-             {
 
-                 ctx.BeginFigure(startPoint, false);
 
-                 ctx.ArcTo(endPoint, new Size(radius, radius), 0, sweepAngle > 180, SweepDirection.Clockwise);
 
-             }
 
-             // Draw the progress arc
 
-             context.DrawGeometry(null, progressPen, geometry);
 
-         }
 
-     }
 
- }
 
 
  |