进度条组件完成

This commit is contained in:
polarboy 2024-06-22 09:42:32 +08:00
parent f88a6662a6
commit f8546846bc
11 changed files with 172 additions and 19 deletions

View File

@ -63,6 +63,19 @@
</WrapPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Dynamic"
Description="A dynamic progress bar is better.">
<StackPanel Orientation="Vertical" Spacing="10">
<atom:ProgressBar Value="{Binding ProgressValue}" Minimum="0" Maximum="100"/>
<atom:CircleProgress Value="{Binding ProgressValue}" Minimum="0" Maximum="100"/>
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Button SizeType="Small" Command="{Binding SubProgressValue}">Sub</atom:Button>
<atom:Button SizeType="Small" Command="{Binding AddProgressValue}">Add</atom:Button>
</StackPanel>
</StackPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Custom text format"
Description="You can set a custom text by setting the format prop.">
@ -86,8 +99,8 @@
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Dashboard"
Description="By setting type=dashboard, you can get a dashboard style of progress easily. Modify gapDegree to set the degree of gap.">
Title="Progress bar with success segment"
Description="Show several parts of progress with different status.">
<StackPanel Orientation="Vertical" Spacing="10">
<atom:ProgressBar Value="60" Minimum="0" Maximum="100" SuccessThreshold="30" />
<WrapPanel Orientation="Horizontal">
@ -277,5 +290,50 @@
</StackPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Vertical progress bar"
Description="Ordinary step progress bar, supports position specification of additional areas">
<StackPanel Orientation="Horizontal" Spacing="10" Height="300">
<atom:StepsProgressBar Value="100" Minimum="0" Maximum="100" Steps="10" Orientation="Vertical" PercentPosition="End"/>
<atom:StepsProgressBar Value="55" Minimum="0" Maximum="100" Steps="5" Orientation="Vertical"/>
<atom:StepsProgressBar Value="55" Minimum="0" Maximum="100" Steps="10" Orientation="Vertical" SizeType="Small"/>
<atom:StepsProgressBar Value="55" Minimum="0" Maximum="100" Steps="6" Orientation="Vertical" PercentPosition="Start"/>
<atom:StepsProgressBar Value="55" Minimum="0" Maximum="100" Steps="6" Orientation="Vertical" PercentPosition="Center"/>
<atom:StepsProgressBar Value="100" Minimum="0" Maximum="100" Steps="6" Orientation="Vertical" PercentPosition="Start"/>
</StackPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="toggle disabled status"
Description="The progress bar is in the disabled state and uses the disabled style.">
<StackPanel Orientation="Vertical" Spacing="10">
<atom:ProgressBar Value="30" Minimum="0" Maximum="100" IsEnabled="{Binding ToggleStatus}"/>
<atom:ProgressBar Value="50" Minimum="0" Maximum="100" IsEnabled="{Binding ToggleStatus}"/>
<atom:ProgressBar Value="70" Minimum="0" Maximum="100" Status="Exception" IsEnabled="{Binding ToggleStatus}"/>
<atom:ProgressBar Value="100" Minimum="0" Maximum="100" IsEnabled="{Binding ToggleStatus}"/>
<atom:StepsProgressBar Value="30" Minimum="0" Maximum="100" Steps="10" IsEnabled="{Binding ToggleStatus}"/>
<atom:StepsProgressBar Value="50" Minimum="0" Maximum="100" Steps="10" IsEnabled="{Binding ToggleStatus}"/>
<atom:StepsProgressBar Value="70" Minimum="0" Maximum="100" Steps="10" Status="Exception" IsEnabled="{Binding ToggleStatus}"/>
<atom:StepsProgressBar Value="100" Minimum="0" Maximum="100" Steps="10" IsEnabled="{Binding ToggleStatus}"/>
<WrapPanel Orientation="Horizontal">
<atom:CircleProgress Value="75" Minimum="0" Maximum="100" SizeType="Middle" IsEnabled="{Binding ToggleStatus}"/>
<atom:CircleProgress Value="70" Minimum="0" Maximum="100" SizeType="Middle" Status="Exception" IsEnabled="{Binding ToggleStatus}"/>
<atom:CircleProgress Value="100" Minimum="0" Maximum="100" SizeType="Middle" IsEnabled="{Binding ToggleStatus}"/>
</WrapPanel>
<WrapPanel Orientation="Horizontal">
<atom:DashboardProgress Value="75" Minimum="0" Maximum="100" SizeType="Middle" IsEnabled="{Binding ToggleStatus}"/>
<atom:DashboardProgress Value="70" Minimum="0" Maximum="100" SizeType="Middle" Status="Exception" IsEnabled="{Binding ToggleStatus}"/>
<atom:DashboardProgress Value="100" Minimum="0" Maximum="100" SizeType="Middle" IsEnabled="{Binding ToggleStatus}"/>
</WrapPanel>
<atom:Button Margin="0, 10, 0, 0"
Text="{Binding ToggleDisabledText}"
Command="{Binding ToggleEnabledStatus}"/>
</StackPanel>
</showcase:ShowCaseItem>
</showcase:ShowCasePanel>
</UserControl>

View File

@ -1,6 +1,8 @@
using AtomUI.Controls;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
namespace AtomUI.Demo.Desktop.ShowCase;
@ -20,6 +22,33 @@ public partial class ProgressBarShowCase : UserControl
public PercentPosition OutterCenterPercentPosition { get; set; }
public PercentPosition OutterEndPercentPosition { get; set; }
public static readonly StyledProperty<double> ProgressValueProperty =
AvaloniaProperty.Register<ProgressBarShowCase, double>(nameof(ProgressValue), 30);
public static readonly StyledProperty<string> ToggleDisabledTextProperty =
AvaloniaProperty.Register<ProgressBarShowCase, string>(nameof(ToggleDisabledText), "Disable");
public static readonly StyledProperty<bool> ToggleStatusProperty =
AvaloniaProperty.Register<ProgressBarShowCase, bool>(nameof(ToggleStatus), true);
public double ProgressValue
{
get => GetValue(ProgressValueProperty);
set => SetValue(ProgressValueProperty, value);
}
public string ToggleDisabledText
{
get => GetValue(ToggleDisabledTextProperty);
set => SetValue(ToggleDisabledTextProperty, value);
}
public bool ToggleStatus
{
get => GetValue(ToggleStatusProperty);
set => SetValue(ToggleStatusProperty, value);
}
public ProgressBarShowCase()
{
InitializeComponent();
@ -81,4 +110,28 @@ public partial class ProgressBarShowCase : UserControl
Alignment = LinePercentAlignment.End
};
}
public void AddProgressValue()
{
var value = ProgressValue;
value += 10;
ProgressValue = Math.Min(value, 100);
}
public void SubProgressValue()
{
var value = ProgressValue;
value -= 10;
ProgressValue = Math.Max(value, 0);
}
public void ToggleEnabledStatus()
{
ToggleStatus = !ToggleStatus;
if (ToggleStatus) {
ToggleDisabledText = "Disable";
} else {
ToggleDisabledText = "Enable";
}
}
}

View File

@ -25,6 +25,8 @@ public partial class Button : IWaveAdornerInfoProvider, IControlCustomStyle
void IControlCustomStyle.SetupUi()
{
HorizontalAlignment = HorizontalAlignment.Left;
VerticalAlignment = VerticalAlignment.Bottom;
Cursor = new Cursor(StandardCursorType.Hand);
_customStyle.CollectStyleState();
CreateMainLayout();

View File

@ -39,8 +39,7 @@ public abstract partial class AbstractCircleProgress : AbstractProgressBar
{
HorizontalAlignmentProperty.OverrideDefaultValue<AbstractCircleProgress>(HorizontalAlignment.Left);
VerticalAlignmentProperty.OverrideDefaultValue<AbstractCircleProgress>(VerticalAlignment.Top);
AffectsRender<AbstractCircleProgress>(IndicatorAngleProperty,
StepCountProperty,
AffectsMeasure<AbstractCircleProgress>(StepCountProperty,
StepGapProperty);
}
@ -106,7 +105,6 @@ public abstract partial class AbstractCircleProgress : AbstractProgressBar
if (ShowProgressInfo) {
_percentageLabel!.Measure(availableSize);
}
return new Size(targetSize, targetSize);
}

View File

@ -87,9 +87,7 @@ public abstract partial class AbstractLineProgress : AbstractProgressBar
EffectiveSizeType = CalculateEffectiveSizeType(sizeValue);
}
if (_extraInfoSize == Size.Infinity) {
_extraInfoSize = CalculateExtraInfoSize(FontSize);
}
SetupAlignment();
NotifyOrientationChanged();
}
@ -112,9 +110,7 @@ public abstract partial class AbstractLineProgress : AbstractProgressBar
EffectiveSizeType = CalculateEffectiveSizeType(e.GetNewValue<double>());
CalculateStrokeThickness();
} else if (e.Property == EffectiveSizeTypeProperty) {
if (_extraInfoSize == Size.Infinity) {
_extraInfoSize = CalculateExtraInfoSize(FontSize);
}
} else if (e.Property == OrientationProperty) {
NotifyOrientationChanged();
}
@ -187,4 +183,21 @@ public abstract partial class AbstractLineProgress : AbstractProgressBar
}
protected virtual void NotifyOrientationChanged() {}
private bool _lastCompletedStatus = false;
protected override void NotifyHandleExtraInfoVisibility()
{
base.NotifyHandleExtraInfoVisibility();
var currentStatus = false;
if (NumberUtils.FuzzyEqual(Value, Maximum)) {
currentStatus = true;
} else {
currentStatus = false;
}
if (currentStatus != _lastCompletedStatus) {
_lastCompletedStatus = currentStatus;
_extraInfoSize = CalculateExtraInfoSize(FontSize);
}
}
}

View File

@ -7,6 +7,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Threading;
namespace AtomUI.Controls;
@ -204,7 +205,8 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
TrailColorProperty,
StrokeThicknessProperty,
SuccessThresholdBrushProperty,
SuccessThresholdProperty);
SuccessThresholdProperty,
ValueProperty);
}
public AbstractProgressBar()
@ -246,6 +248,7 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
Padding = new Thickness(0),
VerticalContentAlignment = VerticalAlignment.Center,
};
BindUtils.RelayBind(this, "IsEnabled", _percentageLabel);
}
return _percentageLabel;
@ -282,7 +285,6 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
_exceptionCompletedIcon.IsVisible = true;
_successCompletedIcon.IsVisible = false;
} else {
_percentageLabel.Content = string.Format(ProgressTextFormat, _percentage);
if (NumberUtils.FuzzyEqual(100, Percentage)) {
_percentageLabel.IsVisible = false;
_successCompletedIcon.IsVisible = true;
@ -291,11 +293,12 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA
_exceptionCompletedIcon.IsVisible = false;
_percentageLabel.IsVisible = true;
}
_percentageLabel.Content = string.Format(ProgressTextFormat, _percentage);
}
NotifyHandleExtraInfoVisibility();
}
}
protected virtual void NotifyHandleExtraInfoVisibility() {}
protected virtual void NotifyHandleExtraInfoVisibility() { }
}

View File

@ -1,9 +1,13 @@
using AtomUI.Data;
using AtomUI.Icon;
using AtomUI.Media;
using AtomUI.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Media;
namespace AtomUI.Controls;
@ -46,6 +50,10 @@ public partial class AbstractProgressBar : IControlCustomStyle
void IControlCustomStyle.SetupTransitions()
{
var transitions = new Transitions();
transitions.Add(AnimationUtils.CreateTransition<DoubleTransition>(ValueProperty, GlobalResourceKey.MotionDurationVerySlow, new ExponentialEaseOut()));
transitions.Add(AnimationUtils.CreateTransition<SolidColorBrushTransition>(IndicatorBarBrushProperty, GlobalResourceKey.MotionDurationFast));
NotifySetupTransitions(ref transitions);
Transitions = transitions;
}
@ -121,15 +129,28 @@ public partial class AbstractProgressBar : IControlCustomStyle
} else {
_tokenResourceBinder.AddBinding(GrooveBrushProperty, ProgressBarResourceKey.RemainingColor);
}
_tokenResourceBinder.AddBinding(IndicatorBarBrushProperty, ProgressBarResourceKey.DefaultColor);
if (Status == ProgressStatus.Success || NumberUtils.FuzzyEqual(Value, Maximum)) {
_tokenResourceBinder.AddBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorSuccess);
} else if (Status == ProgressStatus.Exception) {
_tokenResourceBinder.AddBinding(IndicatorBarBrushProperty, GlobalResourceKey.ColorError);
} else {
_tokenResourceBinder.AddBinding(IndicatorBarBrushProperty, ProgressBarResourceKey.DefaultColor);
}
_tokenResourceBinder.AddBinding(ForegroundProperty, GlobalResourceKey.ColorTextLabel);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Normal;
_successCompletedIcon!.IconMode = IconMode.Normal;
}
} else {
_tokenResourceBinder.AddBinding(GrooveBrushProperty, GlobalResourceKey.ColorBgContainerDisabled);
_tokenResourceBinder.AddBinding(IndicatorBarBrushProperty, GlobalResourceKey.ControlItemBgActiveDisabled);
_tokenResourceBinder.AddBinding(ForegroundProperty, GlobalResourceKey.ColorTextDisabled);
if (_initialized) {
_exceptionCompletedIcon!.IconMode = IconMode.Disabled;
_successCompletedIcon!.IconMode = IconMode.Disabled;
}
}
}

View File

@ -10,6 +10,7 @@ public class CircleProgress : AbstractCircleProgress
protected override void RenderGroove(DrawingContext context)
{
var controlRect = new Rect(new Point(0, 0), DesiredSize);
_currentGrooveRect = GetProgressBarRect(controlRect).Deflate(StrokeThickness / 2);
_currentGrooveRect = new Rect(_currentGrooveRect.Position, new Size(Math.Floor(_currentGrooveRect.Size.Width),
Math.Floor(_currentGrooveRect.Size.Height)));
@ -55,6 +56,7 @@ public class CircleProgress : AbstractCircleProgress
{
LineCap = StrokeLineCap
};
double startAngle = -90;
context.DrawArc(pen, _currentGrooveRect, startAngle, IndicatorAngle);

View File

@ -280,7 +280,7 @@ public partial class ProgressBar : AbstractLineProgress
} else if (PercentPosition.Alignment == LinePercentAlignment.Center) {
deflateBottom = percentLabelHeight;
} else if (PercentPosition.Alignment == LinePercentAlignment.End) {
deflateRight = percentLabelWidth + _lineExtraInfoMargin;;
deflateRight = percentLabelWidth + _lineExtraInfoMargin;
}
}
}
@ -537,6 +537,7 @@ public partial class ProgressBar : AbstractLineProgress
protected override void NotifyHandleExtraInfoVisibility()
{
base.NotifyHandleExtraInfoVisibility();
if (PercentPosition.IsInner) {
_exceptionCompletedIcon!.IsVisible = false;
_successCompletedIcon!.IsVisible = false;

View File

@ -31,7 +31,7 @@ public abstract class RangeBaseControl : StyledControl
/// </summary>
public static readonly StyledProperty<double> ValueProperty =
AvaloniaProperty.Register<RangeBaseControl, double>(nameof(Value),
defaultBindingMode: BindingMode.TwoWay,
defaultBindingMode: BindingMode.OneWay,
coerce: CoerceValue);
/// <summary>

View File

@ -1,4 +1,5 @@
using AtomUI.ColorSystem;
using AtomUI.Media;
using AtomUI.Styling;
using Avalonia;
using Avalonia.Animation;
@ -94,7 +95,8 @@ public static class AnimationUtils
var transition = new T()
{
Property = targetProperty,
Easing = easing
Easing = easing,
};
var application = Application.Current;
if (application is not null) {