Browse Source

progress ring thing

pull/55/head
Ionite 1 year ago
parent
commit
fca50b94b2
No known key found for this signature in database
  1. 129
      StabilityMatrix.Avalonia/Controls/ProgressRing.cs
  2. 20
      StabilityMatrix.Avalonia/Converters/FitSquarelyWithinAspectRatioConverter.cs
  3. 113
      StabilityMatrix.Avalonia/Styles/ProgressRing.axaml

129
StabilityMatrix.Avalonia/Controls/ProgressRing.cs

@ -0,0 +1,129 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
namespace StabilityMatrix.Avalonia.Controls;
/// <summary>
/// A control used to indicate the progress of an operation.
/// </summary>
[PseudoClasses(":preserveaspect", ":indeterminate")]
public class ProgressRing : RangeBase
{
public static readonly StyledProperty<bool> IsIndeterminateProperty =
ProgressBar.IsIndeterminateProperty.AddOwner<ProgressRing>();
public static readonly StyledProperty<bool> PreserveAspectProperty =
AvaloniaProperty.Register<ProgressRing, bool>(nameof(PreserveAspect), true);
public static readonly StyledProperty<double> ValueAngleProperty =
AvaloniaProperty.Register<ProgressRing, double>(nameof(ValueAngle), 0);
public static readonly StyledProperty<double> StartAngleProperty =
AvaloniaProperty.Register<ProgressRing, double>(nameof(StartAngle), 0);
public static readonly StyledProperty<double> EndAngleProperty =
AvaloniaProperty.Register<ProgressRing, double>(nameof(EndAngle), 360);
static ProgressRing()
{
MinimumProperty.Changed.AddClassHandler<ProgressRing>(OnMinimumPropertyChanged);
MaximumProperty.Changed.AddClassHandler<ProgressRing>(OnMaximumPropertyChanged);
ValueProperty.Changed.AddClassHandler<ProgressRing>(OnValuePropertyChanged);
MaximumProperty.Changed.AddClassHandler<ProgressRing>(OnStartAnglePropertyChanged);
MaximumProperty.Changed.AddClassHandler<ProgressRing>(OnEndAnglePropertyChanged);
}
public ProgressRing()
{
UpdatePseudoClasses(IsIndeterminate, PreserveAspect);
}
public bool IsIndeterminate
{
get => GetValue(IsIndeterminateProperty);
set => SetValue(IsIndeterminateProperty, value);
}
public bool PreserveAspect
{
get => GetValue(PreserveAspectProperty);
set => SetValue(PreserveAspectProperty, value);
}
public double ValueAngle
{
get => GetValue(ValueAngleProperty);
private set => SetValue(ValueAngleProperty, value);
}
public double StartAngle
{
get => GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
public double EndAngle
{
get => GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
var e = change as AvaloniaPropertyChangedEventArgs<bool>;
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);
}
}
static void OnMinimumPropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.Minimum = (double)e.NewValue;
}
static void OnMaximumPropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.Maximum = (double)e.NewValue;
}
static void OnValuePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.ValueAngle = ((double)e.NewValue - sender.Minimum) * (sender.EndAngle - sender.StartAngle) / (sender.Maximum - sender.Minimum);
}
static void OnStartAnglePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.StartAngle = (double)e.NewValue;
}
static void OnEndAnglePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.EndAngle = (double)e.NewValue;
}
}

20
StabilityMatrix.Avalonia/Converters/FitSquarelyWithinAspectRatioConverter.cs

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Data.Converters;
namespace StabilityMatrix.Avalonia.Converters;
public class FitSquarelyWithinAspectRatioConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var bounds = value is Rect rect ? rect : default;
return Math.Min(bounds.Width, bounds.Height);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

113
StabilityMatrix.Avalonia/Styles/ProgressRing.axaml

@ -0,0 +1,113 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:StabilityMatrix.Avalonia.Converters"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
x:CompileBindings="True">
<Design.PreviewWith>
<StackPanel>
<Border Padding="20">
<controls:ProgressRing StartAngle="90" EndAngle="270" Value="50" Foreground="Red" BorderThickness="5" Width="100" Height="100"></controls:ProgressRing>
</Border>
<Border Padding="20">
<controls:ProgressRing IsIndeterminate="True" BorderThickness="10" Width="100" Height="100"></controls:ProgressRing>
</Border>
</StackPanel>
</Design.PreviewWith>
<Styles.Resources>
<converters:FitSquarelyWithinAspectRatioConverter x:Key="FitSquarelyWithinAspectRatioConverter"/>
</Styles.Resources>
<Style Selector="controls|ProgressRing">
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="BorderThickness" Value="9" />
<Setter Property="MinHeight" Value="16" />
<Setter Property="MinWidth" Value="16" />
<Setter Property="Template">
<ControlTemplate>
<Panel x:Name="FluentRingRoot">
<Ellipse x:Name="Track"
Stroke="{TemplateBinding Background}"
StrokeThickness="{ReflectionBinding BorderThickness.Left, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<Arc x:Name="Fill"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="{ReflectionBinding BorderThickness.Left, RelativeSource={RelativeSource Mode=TemplatedParent}}"
StrokeLineCap="Round" />
</Panel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="controls|ProgressRing:not(:indeterminate) /template/ Arc#Fill">
<Setter Property="StartAngle" Value="{ReflectionBinding StartAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
<Setter Property="SweepAngle" Value="{ReflectionBinding ValueAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
</Style>
<Style Selector="controls|ProgressRing:preserveaspect">
<Setter Property="MinWidth" Value="32" />
<Setter Property="MinHeight" Value="32" />
</Style>
<Style Selector="controls|ProgressRing:preserveaspect /template/ Panel#FluentRingRoot">
<Setter Property="Width" Value="{TemplateBinding Bounds, Converter={StaticResource FitSquarelyWithinAspectRatioConverter}}"/>
<Setter Property="Height" Value="{ReflectionBinding Width, RelativeSource={RelativeSource Mode=Self}}"/>
</Style>
<Style Selector="controls|ProgressRing[IsEnabled=True]:indeterminate /template/ Arc#Fill">
<Style.Animations>
<Animation Duration="0:0:5" Easing="LinearEasing" IterationCount="INFINITE" FillMode="Both">
<KeyFrame Cue="0%">
<Setter Property="StartAngle" Value="-720"/>
<Setter Property="SweepAngle" Value="0"/>
</KeyFrame>
<KeyFrame Cue="12.5%">
<Setter Property="StartAngle" Value="-540"/>
<Setter Property="SweepAngle" Value="50"/>
</KeyFrame>
<KeyFrame Cue="25%">
<Setter Property="StartAngle" Value="-360"/>
<Setter Property="SweepAngle" Value="100"/>
</KeyFrame>
<KeyFrame Cue="32.5%">
<Setter Property="StartAngle" Value="-180"/>
<Setter Property="SweepAngle" Value="50"/>
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="StartAngle" Value="0"/>
<Setter Property="SweepAngle" Value="5"/>
</KeyFrame>
<KeyFrame Cue="62.5%">
<Setter Property="StartAngle" Value="180"/>
<Setter Property="SweepAngle" Value="50"/>
</KeyFrame>
<KeyFrame Cue="75%">
<Setter Property="StartAngle" Value="360"/>
<Setter Property="SweepAngle" Value="100"/>
</KeyFrame>
<KeyFrame Cue="87.5%">
<Setter Property="StartAngle" Value="540"/>
<Setter Property="SweepAngle" Value="50"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="StartAngle" Value="720"/>
<Setter Property="SweepAngle" Value="0"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="controls|ProgressRing[IsEnabled=True] /template/ Ellipse#Track">
<Style.Animations>
<Animation Duration="0:0:1" IterationCount="INFINITE">
<KeyFrame Cue="0%">
<Setter Property="Opacity" Value="0.99"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Opacity" Value="1"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Styles>
Loading…
Cancel
Save