diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml index 252d6e1..8ae0f80 100644 --- a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml @@ -63,6 +63,19 @@ + + + + + + Sub + Add + + + + @@ -86,8 +99,8 @@ + Title="Progress bar with success segment" + Description="Show several parts of progress with different status."> @@ -277,5 +290,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs index a7edcd0..d1dac16 100644 --- a/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs +++ b/Samples/AtomUI.Demo.Desktop/ShowCase/ProgressBarShowCase.axaml.cs @@ -1,6 +1,8 @@ using AtomUI.Controls; +using Avalonia; using Avalonia.Controls; using Avalonia.Media; +using CommunityToolkit.Mvvm.ComponentModel; namespace AtomUI.Demo.Desktop.ShowCase; @@ -19,6 +21,33 @@ public partial class ProgressBarShowCase : UserControl public PercentPosition OutterStartPercentPosition { get; set; } public PercentPosition OutterCenterPercentPosition { get; set; } public PercentPosition OutterEndPercentPosition { get; set; } + + public static readonly StyledProperty ProgressValueProperty = + AvaloniaProperty.Register(nameof(ProgressValue), 30); + + public static readonly StyledProperty ToggleDisabledTextProperty = + AvaloniaProperty.Register(nameof(ToggleDisabledText), "Disable"); + + public static readonly StyledProperty ToggleStatusProperty = + AvaloniaProperty.Register(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() { @@ -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"; + } + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/Buttons/ButtonStyle.cs b/src/AtomUI.Controls/Buttons/ButtonStyle.cs index cb35937..b6d3014 100644 --- a/src/AtomUI.Controls/Buttons/ButtonStyle.cs +++ b/src/AtomUI.Controls/Buttons/ButtonStyle.cs @@ -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(); diff --git a/src/AtomUI.Controls/ProgressBar/AbstractCircleProgress.cs b/src/AtomUI.Controls/ProgressBar/AbstractCircleProgress.cs index 01466e2..bfb1833 100644 --- a/src/AtomUI.Controls/ProgressBar/AbstractCircleProgress.cs +++ b/src/AtomUI.Controls/ProgressBar/AbstractCircleProgress.cs @@ -39,8 +39,7 @@ public abstract partial class AbstractCircleProgress : AbstractProgressBar { HorizontalAlignmentProperty.OverrideDefaultValue(HorizontalAlignment.Left); VerticalAlignmentProperty.OverrideDefaultValue(VerticalAlignment.Top); - AffectsRender(IndicatorAngleProperty, - StepCountProperty, + AffectsMeasure(StepCountProperty, StepGapProperty); } @@ -106,7 +105,6 @@ public abstract partial class AbstractCircleProgress : AbstractProgressBar if (ShowProgressInfo) { _percentageLabel!.Measure(availableSize); } - return new Size(targetSize, targetSize); } diff --git a/src/AtomUI.Controls/ProgressBar/AbstractLineProgress.cs b/src/AtomUI.Controls/ProgressBar/AbstractLineProgress.cs index 57238e8..554be79 100644 --- a/src/AtomUI.Controls/ProgressBar/AbstractLineProgress.cs +++ b/src/AtomUI.Controls/ProgressBar/AbstractLineProgress.cs @@ -87,9 +87,7 @@ public abstract partial class AbstractLineProgress : AbstractProgressBar EffectiveSizeType = CalculateEffectiveSizeType(sizeValue); } - if (_extraInfoSize == Size.Infinity) { - _extraInfoSize = CalculateExtraInfoSize(FontSize); - } + _extraInfoSize = CalculateExtraInfoSize(FontSize); SetupAlignment(); NotifyOrientationChanged(); } @@ -112,9 +110,7 @@ public abstract partial class AbstractLineProgress : AbstractProgressBar EffectiveSizeType = CalculateEffectiveSizeType(e.GetNewValue()); CalculateStrokeThickness(); } else if (e.Property == EffectiveSizeTypeProperty) { - if (_extraInfoSize == Size.Infinity) { - _extraInfoSize = CalculateExtraInfoSize(FontSize); - } + _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); + } + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/ProgressBar/AbstractProgressBar.cs b/src/AtomUI.Controls/ProgressBar/AbstractProgressBar.cs index 1b02432..a9ab997 100644 --- a/src/AtomUI.Controls/ProgressBar/AbstractProgressBar.cs +++ b/src/AtomUI.Controls/ProgressBar/AbstractProgressBar.cs @@ -7,6 +7,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; +using Avalonia.Threading; namespace AtomUI.Controls; @@ -138,7 +139,7 @@ public abstract partial class AbstractProgressBar : RangeBaseControl, ISizeTypeA get => GetValue(StatusProperty); set => SetValue(StatusProperty, value); } - + protected double _percentage; /// @@ -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() { } } \ No newline at end of file diff --git a/src/AtomUI.Controls/ProgressBar/AbstractProgressBarStyle.cs b/src/AtomUI.Controls/ProgressBar/AbstractProgressBarStyle.cs index 9545885..e609619 100644 --- a/src/AtomUI.Controls/ProgressBar/AbstractProgressBarStyle.cs +++ b/src/AtomUI.Controls/ProgressBar/AbstractProgressBarStyle.cs @@ -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(ValueProperty, GlobalResourceKey.MotionDurationVerySlow, new ExponentialEaseOut())); + transitions.Add(AnimationUtils.CreateTransition(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; + } } } diff --git a/src/AtomUI.Controls/ProgressBar/CircleProgress.cs b/src/AtomUI.Controls/ProgressBar/CircleProgress.cs index 66ba106..a350530 100644 --- a/src/AtomUI.Controls/ProgressBar/CircleProgress.cs +++ b/src/AtomUI.Controls/ProgressBar/CircleProgress.cs @@ -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); diff --git a/src/AtomUI.Controls/ProgressBar/ProgressBar.cs b/src/AtomUI.Controls/ProgressBar/ProgressBar.cs index a5ecac2..5b19fce 100644 --- a/src/AtomUI.Controls/ProgressBar/ProgressBar.cs +++ b/src/AtomUI.Controls/ProgressBar/ProgressBar.cs @@ -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; diff --git a/src/AtomUI.Controls/RangeBaseControl.cs b/src/AtomUI.Controls/RangeBaseControl.cs index e9c1515..ac1f948 100644 --- a/src/AtomUI.Controls/RangeBaseControl.cs +++ b/src/AtomUI.Controls/RangeBaseControl.cs @@ -31,7 +31,7 @@ public abstract class RangeBaseControl : StyledControl /// public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register(nameof(Value), - defaultBindingMode: BindingMode.TwoWay, + defaultBindingMode: BindingMode.OneWay, coerce: CoerceValue); /// diff --git a/src/AtomUI/Utils/AnimationUtils.cs b/src/AtomUI/Utils/AnimationUtils.cs index 86c1464..d01195d 100644 --- a/src/AtomUI/Utils/AnimationUtils.cs +++ b/src/AtomUI/Utils/AnimationUtils.cs @@ -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) {