Browse Source

Fix ProgressRing high cpu usage in value mode

pull/324/head
Ionite 12 months ago
parent
commit
e15dabd06a
No known key found for this signature in database
  1. 135
      StabilityMatrix.Avalonia/Controls/ProgressRing.cs
  2. 46
      StabilityMatrix.Avalonia/Styles/ProgressRing.axaml

135
StabilityMatrix.Avalonia/Controls/ProgressRing.cs

@ -1,8 +1,10 @@
using System.Diagnostics.CodeAnalysis;
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;
@ -13,70 +15,102 @@ namespace StabilityMatrix.Avalonia.Controls;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class ProgressRing : RangeBase
{
private Arc? fillArc;
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));
public static readonly StyledProperty<double> StartAngleProperty =
AvaloniaProperty.Register<ProgressRing, double>(nameof(StartAngle));
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 static readonly StyledProperty<bool> PreserveAspectProperty = AvaloniaProperty.Register<
ProgressRing,
bool
>(nameof(PreserveAspect), true);
public bool PreserveAspect
{
get => GetValue(PreserveAspectProperty);
set => SetValue(PreserveAspectProperty, value);
}
public double ValueAngle
public static readonly StyledProperty<double> StrokeThicknessProperty =
Shape.StrokeThicknessProperty.AddOwner<ProgressRing>();
public double StrokeThickness
{
get => GetValue(ValueAngleProperty);
private set => SetValue(ValueAngleProperty, value);
get => GetValue(StrokeThicknessProperty);
set => SetValue(StrokeThicknessProperty, value);
}
public static readonly StyledProperty<double> StartAngleProperty = AvaloniaProperty.Register<
ProgressRing,
double
>(nameof(StartAngle));
public double StartAngle
{
get => GetValue(StartAngleProperty);
set => SetValue(StartAngleProperty, value);
}
public static readonly StyledProperty<double> SweepAngleProperty = AvaloniaProperty.Register<
ProgressRing,
double
>(nameof(SweepAngle));
public double SweepAngle
{
get => GetValue(SweepAngleProperty);
set => SetValue(SweepAngleProperty, value);
}
public static readonly StyledProperty<double> EndAngleProperty = AvaloniaProperty.Register<
ProgressRing,
double
>(nameof(EndAngle), 360);
public double EndAngle
{
get => GetValue(EndAngleProperty);
set => SetValue(EndAngleProperty, value);
}
static ProgressRing()
{
AffectsRender<ProgressRing>(SweepAngleProperty, StartAngleProperty, EndAngleProperty);
ValueProperty.Changed.AddClassHandler<ProgressRing>(OnValuePropertyChanged);
SweepAngleProperty.Changed.AddClassHandler<ProgressRing>(OnSweepAnglePropertyChanged);
}
public ProgressRing()
{
UpdatePseudoClasses(IsIndeterminate, PreserveAspect);
}
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
fillArc = e.NameScope.Find<Arc>("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<bool>;
if (e is null) return;
if (e is null)
return;
if (e.Property == IsIndeterminateProperty)
{
@ -88,9 +122,7 @@ public class ProgressRing : RangeBase
}
}
private void UpdatePseudoClasses(
bool? isIndeterminate,
bool? preserveAspect)
private void UpdatePseudoClasses(bool? isIndeterminate, bool? preserveAspect)
{
if (isIndeterminate.HasValue)
{
@ -103,28 +135,25 @@ public class ProgressRing : RangeBase
}
}
private static void OnMinimumPropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.Minimum = (double) e.NewValue!;
}
private static void OnMaximumPropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.Maximum = (double) e.NewValue!;
}
private static void OnValuePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
{
sender.ValueAngle = ((double) e.NewValue! - sender.Minimum) * (sender.EndAngle - sender.StartAngle) / (sender.Maximum - sender.Minimum);
}
private static void OnStartAnglePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
private static void OnValuePropertyChanged(
ProgressRing sender,
AvaloniaPropertyChangedEventArgs e
)
{
sender.StartAngle = (double) e.NewValue!;
sender.SweepAngle =
((double)e.NewValue! - sender.Minimum)
* (sender.EndAngle - sender.StartAngle)
/ (sender.Maximum - sender.Minimum);
}
private static void OnEndAnglePropertyChanged(ProgressRing sender, AvaloniaPropertyChangedEventArgs e)
private static void OnSweepAnglePropertyChanged(
ProgressRing sender,
AvaloniaPropertyChangedEventArgs e
)
{
sender.EndAngle = (double) e.NewValue!;
if (sender.fillArc is { } arc)
{
arc.SweepAngle = Math.Round(e.GetNewValue<double>());
}
}
}

46
StabilityMatrix.Avalonia/Styles/ProgressRing.axaml

@ -7,10 +7,13 @@
<Design.PreviewWith>
<StackPanel>
<Border Padding="20">
<controls:ProgressRing StartAngle="90" EndAngle="270" Value="50" Foreground="Red" BorderThickness="5" Width="100" Height="100" />
<controls:ProgressRing Value="50" />
</Border>
<Border Padding="20">
<controls:ProgressRing IsIndeterminate="True" BorderThickness="10" Width="100" Height="100" />
<controls:ProgressRing StartAngle="90" EndAngle="450" Value="50" Foreground="Red" StrokeThickness="3" Width="100" Height="100" />
</Border>
<Border Padding="20">
<controls:ProgressRing IsIndeterminate="True" StrokeThickness="10" Width="100" Height="100" />
</Border>
</StackPanel>
</Design.PreviewWith>
@ -22,27 +25,40 @@
<Style Selector="controls|ProgressRing">
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="BorderThickness" Value="9" />
<Setter Property="StrokeThickness" Value="5" />
<Setter Property="MinHeight" Value="16" />
<Setter Property="MinWidth" Value="16" />
<Setter Property="Transitions">
<Transitions>
<DoubleTransition
Property="SweepAngle"
Duration="0:0:0.267">
<DoubleTransition.Easing>
<LinearEasing/>
</DoubleTransition.Easing>
</DoubleTransition>
</Transitions>
</Setter>
<Setter Property="Template">
<ControlTemplate>
<Panel x:Name="FluentRingRoot">
<Ellipse x:Name="Track"
<Panel x:Name="PART_RingRoot">
<Ellipse x:Name="PART_Track"
Stroke="{TemplateBinding Background}"
StrokeThickness="{Binding BorderThickness.Left, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
<Arc x:Name="Fill"
StrokeThickness="{TemplateBinding StrokeThickness}" />
<Arc x:Name="PART_Fill"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="{Binding BorderThickness.Left, RelativeSource={RelativeSource Mode=TemplatedParent}}"
StrokeThickness="{TemplateBinding StrokeThickness}"
StrokeLineCap="Round" />
</Panel>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="controls|ProgressRing:not(:indeterminate) /template/ Arc#Fill">
<Setter Property="StartAngle" Value="{Binding StartAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
<Setter Property="SweepAngle" Value="{Binding ValueAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
<Style Selector="controls|ProgressRing:not(:indeterminate) /template/ Arc#PART_Fill">
<!--<Setter Property="StartAngle" Value="{Binding StartAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>-->
<!--<Setter Property="SweepAngle" Value="{Binding SweepAngle, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>-->
</Style>
<Style Selector="controls|ProgressRing:preserveaspect">
@ -50,14 +66,14 @@
<Setter Property="MinHeight" Value="32" />
</Style>
<Style Selector="controls|ProgressRing:preserveaspect /template/ Panel#FluentRingRoot">
<Style Selector="controls|ProgressRing:preserveaspect /template/ Panel#PART_RingRoot">
<Setter Property="Width" Value="{TemplateBinding Bounds, Converter={StaticResource FitSquarelyWithinAspectRatioConverter}}"/>
<Setter Property="Height" Value="{Binding Width, RelativeSource={RelativeSource Mode=Self}}"/>
</Style>
<Style Selector="controls|ProgressRing[IsEnabled=True]:indeterminate /template/ Arc#Fill">
<Style Selector="controls|ProgressRing[IsEnabled=True]:indeterminate /template/ Arc#PART_Fill">
<Style.Animations>
<Animation Duration="0:0:5" Easing="LinearEasing" IterationCount="INFINITE" FillMode="Both">
<Animation Duration="0:0:4.567" Easing="LinearEasing" IterationCount="Infinite" FillMode="Both">
<KeyFrame Cue="0%">
<Setter Property="StartAngle" Value="-720"/>
<Setter Property="SweepAngle" Value="0"/>
@ -98,7 +114,7 @@
</Style.Animations>
</Style>
<Style Selector="controls|ProgressRing[IsEnabled=True] /template/ Ellipse#Track">
<Style Selector="controls|ProgressRing[IsEnabled=True]:not(:indeterminate) /template/ Ellipse#PART_Track">
<Style.Animations>
<Animation Duration="0:0:1" IterationCount="INFINITE">
<KeyFrame Cue="0%">

Loading…
Cancel
Save