using System; using System.Diagnostics.CodeAnalysis; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Controls.Shapes; namespace StabilityMatrix.Avalonia.Controls; /// /// A control used to indicate the progress of an operation. /// [PseudoClasses(":preserveaspect", ":indeterminate")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] public class ProgressRing : RangeBase { private Arc? fillArc; public static readonly StyledProperty IsIndeterminateProperty = ProgressBar .IsIndeterminateProperty .AddOwner(); public bool IsIndeterminate { get => GetValue(IsIndeterminateProperty); set => SetValue(IsIndeterminateProperty, value); } public static readonly StyledProperty PreserveAspectProperty = AvaloniaProperty.Register( nameof(PreserveAspect), true ); public bool PreserveAspect { get => GetValue(PreserveAspectProperty); set => SetValue(PreserveAspectProperty, value); } public static readonly StyledProperty StrokeThicknessProperty = Shape .StrokeThicknessProperty .AddOwner(); public double StrokeThickness { get => GetValue(StrokeThicknessProperty); set => SetValue(StrokeThicknessProperty, value); } public static readonly StyledProperty StartAngleProperty = AvaloniaProperty.Register( nameof(StartAngle) ); public double StartAngle { get => GetValue(StartAngleProperty); set => SetValue(StartAngleProperty, value); } public static readonly StyledProperty SweepAngleProperty = AvaloniaProperty.Register( nameof(SweepAngle) ); public double SweepAngle { get => GetValue(SweepAngleProperty); set => SetValue(SweepAngleProperty, value); } public static readonly StyledProperty EndAngleProperty = AvaloniaProperty.Register( nameof(EndAngle), 360 ); public double EndAngle { get => GetValue(EndAngleProperty); set => SetValue(EndAngleProperty, value); } static ProgressRing() { AffectsRender(SweepAngleProperty, StartAngleProperty, EndAngleProperty); ValueProperty.Changed.AddClassHandler(OnValuePropertyChanged); SweepAngleProperty.Changed.AddClassHandler(OnSweepAnglePropertyChanged); } public ProgressRing() { UpdatePseudoClasses(IsIndeterminate, PreserveAspect); } /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); fillArc = e.NameScope.Find("PART_Fill"); if (fillArc is not null) { fillArc.StartAngle = StartAngle; fillArc.SweepAngle = SweepAngle; } } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); var e = change as AvaloniaPropertyChangedEventArgs; if (e is null) return; if (e.Property == IsIndeterminateProperty) { UpdatePseudoClasses(e.NewValue.GetValueOrDefault(), null); } else if (e.Property == PreserveAspectProperty) { UpdatePseudoClasses(null, e.NewValue.GetValueOrDefault()); } } private void UpdatePseudoClasses(bool? isIndeterminate, bool? preserveAspect) { if (isIndeterminate.HasValue) { PseudoClasses.Set(":indeterminate", isIndeterminate.Value); } if (preserveAspect.HasValue) { PseudoClasses.Set(":preserveaspect", preserveAspect.Value); } } private static void OnValuePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e) { sender.SweepAngle = ((double)e.NewValue! - sender.Minimum) * (sender.EndAngle - sender.StartAngle) / (sender.Maximum - sender.Minimum); } private static void OnSweepAnglePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e) { if (sender.fillArc is { } arc) { arc.SweepAngle = Math.Round(e.GetNewValue()); } } }