mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-29 18:38:16 +08:00
Refactor badge Motion
This commit is contained in:
parent
d604d2aec3
commit
2ef0690a96
@ -63,6 +63,9 @@ internal class BadgeToken : AbstractControlDesignToken
|
||||
public int BadgeRibbonCornerDarkenAmount { get; set; }
|
||||
public Thickness BadgeRibbonTextPadding { get; set; }
|
||||
public Thickness DotBadgeLabelMargin { get; set; }
|
||||
public Thickness CountBadgeTextPadding { get; set; }
|
||||
public CornerRadius CountBadgeCornerRadius { get; set; }
|
||||
public CornerRadius CountBadgeCornerRadiusSM { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -92,5 +95,8 @@ internal class BadgeToken : AbstractControlDesignToken
|
||||
BadgeRibbonCornerDarkenAmount = 15;
|
||||
BadgeRibbonTextPadding = new Thickness(_globalToken.PaddingXS, 0);
|
||||
DotBadgeLabelMargin = new Thickness(_globalToken.MarginXS, 0, 0, 0);
|
||||
CountBadgeTextPadding = new Thickness(_globalToken.PaddingXXS, 0);
|
||||
CountBadgeCornerRadius = new CornerRadius(IndicatorHeight);
|
||||
CountBadgeCornerRadiusSM = new CornerRadius(IndicatorHeightSM);
|
||||
}
|
||||
}
|
@ -1,7 +1,4 @@
|
||||
using AtomUI.Controls.Badge;
|
||||
using AtomUI.Controls.MotionScene;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.MotionScene;
|
||||
using AtomUI.Data;
|
||||
using AtomUI.Theme.Palette;
|
||||
using AtomUI.Theme.Styling;
|
||||
using AtomUI.Utils;
|
||||
@ -48,7 +45,7 @@ public class CountBadge : Control
|
||||
AvaloniaProperty.Register<CountBadge, CountBadgeSize>(nameof(Size));
|
||||
|
||||
public static readonly StyledProperty<bool> BadgeIsVisibleProperty =
|
||||
AvaloniaProperty.Register<CountBadge, bool>(nameof(BadgeIsVisible));
|
||||
AvaloniaProperty.Register<CountBadge, bool>(nameof(BadgeIsVisible), true);
|
||||
|
||||
public string? BadgeColor
|
||||
{
|
||||
@ -117,7 +114,7 @@ public class CountBadge : Control
|
||||
|
||||
private CountBadgeAdorner? _badgeAdorner;
|
||||
private AdornerLayer? _adornerLayer;
|
||||
private bool _animating;
|
||||
private bool _isInitialized;
|
||||
|
||||
static CountBadge()
|
||||
{
|
||||
@ -131,10 +128,17 @@ public class CountBadge : Control
|
||||
public sealed override void ApplyTemplate()
|
||||
{
|
||||
base.ApplyTemplate();
|
||||
if (!_isInitialized)
|
||||
{
|
||||
if (DecoratedTarget is null)
|
||||
{
|
||||
CreateBadgeAdorner();
|
||||
}
|
||||
|
||||
SetupShowZero();
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private CountBadgeAdorner CreateBadgeAdorner()
|
||||
@ -154,123 +158,47 @@ public class CountBadge : Control
|
||||
}
|
||||
|
||||
private void PrepareAdorner()
|
||||
{
|
||||
if (_adornerLayer is null && DecoratedTarget is not null)
|
||||
{
|
||||
var badgeAdorner = CreateBadgeAdorner();
|
||||
if (DecoratedTarget is not null)
|
||||
{
|
||||
_adornerLayer = AdornerLayer.GetAdornerLayer(this);
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdornerLayer.SetAdornedElement(badgeAdorner, this);
|
||||
AdornerLayer.SetIsClipEnabled(badgeAdorner, false);
|
||||
_adornerLayer.Children.Add(badgeAdorner);
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareAdornerWithMotion()
|
||||
{
|
||||
PrepareAdorner();
|
||||
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
|
||||
AbstractMotion motion;
|
||||
var adorner = _badgeAdorner!;
|
||||
if (DecoratedTarget is not null)
|
||||
{
|
||||
var countBadgeZoomBadgeIn = new CountBadgeZoomBadgeIn();
|
||||
countBadgeZoomBadgeIn.ConfigureOpacity(MotionDuration);
|
||||
countBadgeZoomBadgeIn.ConfigureRenderTransform(MotionDuration);
|
||||
motion = countBadgeZoomBadgeIn;
|
||||
adorner.AnimationRenderTransformOrigin = motion.MotionRenderTransformOrigin;
|
||||
badgeAdorner.ApplyToTarget(_adornerLayer, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var countBadgeNoWrapperZoomBadgeIn = new CountBadgeNoWrapperZoomBadgeIn();
|
||||
countBadgeNoWrapperZoomBadgeIn.ConfigureOpacity(MotionDuration);
|
||||
countBadgeNoWrapperZoomBadgeIn.ConfigureRenderTransform(MotionDuration);
|
||||
motion = countBadgeNoWrapperZoomBadgeIn;
|
||||
badgeAdorner.ApplyToTarget(null, this);
|
||||
}
|
||||
}
|
||||
|
||||
var motionActor = new MotionActor(adorner, motion);
|
||||
motionActor.DispatchInSceneLayer = false;
|
||||
motionActor.Completed += (sender, args) =>
|
||||
{
|
||||
adorner.AnimationRenderTransformOrigin = null;
|
||||
_animating = false;
|
||||
};
|
||||
director?.Schedule(motionActor);
|
||||
}
|
||||
|
||||
private void HideAdorner()
|
||||
private void HideAdorner(bool enableMotion)
|
||||
{
|
||||
// 这里需要抛出异常吗?
|
||||
if (_adornerLayer is null || _badgeAdorner is null)
|
||||
if (_badgeAdorner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_adornerLayer.Children.Remove(_badgeAdorner);
|
||||
_adornerLayer = null;
|
||||
}
|
||||
|
||||
private void HideAdornerWithMotion()
|
||||
{
|
||||
if (VisualRoot is null || _animating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_animating = true;
|
||||
var director = Director.Instance;
|
||||
AbstractMotion motion;
|
||||
var adorner = _badgeAdorner!;
|
||||
if (DecoratedTarget is not null)
|
||||
{
|
||||
var countBadgeZoomBadgeOut = new CountBadgeZoomBadgeOut();
|
||||
countBadgeZoomBadgeOut.ConfigureOpacity(MotionDuration);
|
||||
countBadgeZoomBadgeOut.ConfigureRenderTransform(MotionDuration);
|
||||
motion = countBadgeZoomBadgeOut;
|
||||
adorner.AnimationRenderTransformOrigin = motion.MotionRenderTransformOrigin;
|
||||
}
|
||||
else
|
||||
{
|
||||
var countBadgeNoWrapperZoomBadgeOut = new CountBadgeNoWrapperZoomBadgeOut();
|
||||
countBadgeNoWrapperZoomBadgeOut.ConfigureOpacity(MotionDuration);
|
||||
countBadgeNoWrapperZoomBadgeOut.ConfigureRenderTransform(MotionDuration);
|
||||
motion = countBadgeNoWrapperZoomBadgeOut;
|
||||
}
|
||||
|
||||
var motionActor = new MotionActor(adorner, motion);
|
||||
motionActor.DispatchInSceneLayer = false;
|
||||
motionActor.Completed += (sender, args) =>
|
||||
{
|
||||
HideAdorner();
|
||||
adorner.AnimationRenderTransformOrigin = null;
|
||||
_animating = false;
|
||||
};
|
||||
director?.Schedule(motionActor);
|
||||
_badgeAdorner.DetachFromTarget(_adornerLayer, enableMotion);
|
||||
}
|
||||
|
||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToVisualTree(e);
|
||||
if (BadgeIsVisible)
|
||||
{
|
||||
PrepareAdorner();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
HideAdorner();
|
||||
HideAdorner(false);
|
||||
}
|
||||
|
||||
private void SetupTokenBindings()
|
||||
@ -308,38 +236,16 @@ public class CountBadge : Control
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (e.Property == IsVisibleProperty)
|
||||
if (e.Property == BadgeIsVisibleProperty)
|
||||
{
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
if (BadgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetupShowZero();
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideAdorner();
|
||||
}
|
||||
}
|
||||
else if (e.Property == BadgeIsVisibleProperty)
|
||||
{
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdornerWithMotion();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideAdornerWithMotion();
|
||||
HideAdorner(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,19 +262,24 @@ public class CountBadge : Control
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Property == CountProperty)
|
||||
if (e.Property == CountProperty ||
|
||||
e.Property == ShowZeroProperty)
|
||||
{
|
||||
var newCount = e.GetNewValue<int>();
|
||||
if (newCount == 0 && !ShowZero)
|
||||
SetupShowZero();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupShowZero()
|
||||
{
|
||||
if (Count == 0 && !ShowZero)
|
||||
{
|
||||
BadgeIsVisible = false;
|
||||
}
|
||||
else if (newCount > 0)
|
||||
else if (Count > 0)
|
||||
{
|
||||
BadgeIsVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupBadgeColor(string colorStr)
|
||||
{
|
||||
|
@ -1,18 +1,16 @@
|
||||
using System.Globalization;
|
||||
using AtomUI.Media;
|
||||
using AtomUI.Controls.Badge;
|
||||
using AtomUI.MotionScene;
|
||||
using AtomUI.Theme.Styling;
|
||||
using AtomUI.Utils;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Documents;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.TextFormatting;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
internal class CountBadgeAdorner : Control
|
||||
internal class CountBadgeAdorner : TemplatedControl
|
||||
{
|
||||
#region 公共属性定义
|
||||
|
||||
@ -20,17 +18,28 @@ internal class CountBadgeAdorner : Control
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, IBrush?>(
|
||||
nameof(BadgeColor));
|
||||
|
||||
public static readonly DirectProperty<CountBadgeAdorner, int> OverflowCountProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, int>(
|
||||
nameof(OverflowCount),
|
||||
o => o.OverflowCount,
|
||||
(o, v) => o.OverflowCount = v);
|
||||
internal static readonly StyledProperty<Point> OffsetProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, Point>(
|
||||
nameof(Offset));
|
||||
|
||||
public static readonly DirectProperty<CountBadgeAdorner, CountBadgeSize> SizeProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, CountBadgeSize>(
|
||||
nameof(Size),
|
||||
o => o.Size,
|
||||
(o, v) => o.Size = v);
|
||||
public static readonly StyledProperty<int> OverflowCountProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, int>(nameof(OverflowCount));
|
||||
|
||||
public static readonly StyledProperty<CountBadgeSize> SizeProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, CountBadgeSize>(
|
||||
nameof(Size));
|
||||
|
||||
internal static readonly StyledProperty<int> CountProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, int>(
|
||||
nameof(Count));
|
||||
|
||||
internal static readonly StyledProperty<IBrush?> BadgeShadowColorProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, IBrush?>(
|
||||
nameof(BadgeShadowColor));
|
||||
|
||||
internal static readonly StyledProperty<double> BadgeShadowSizeProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, double>(
|
||||
nameof(BadgeShadowSize));
|
||||
|
||||
public IBrush? BadgeColor
|
||||
{
|
||||
@ -44,115 +53,63 @@ internal class CountBadgeAdorner : Control
|
||||
set => SetValue(OffsetProperty, value);
|
||||
}
|
||||
|
||||
private int _overflowCount;
|
||||
|
||||
public int OverflowCount
|
||||
{
|
||||
get => _overflowCount;
|
||||
set => SetAndRaise(OverflowCountProperty, ref _overflowCount, value);
|
||||
get => GetValue(OverflowCountProperty);
|
||||
set => SetValue(OverflowCountProperty, value);
|
||||
}
|
||||
|
||||
public CountBadgeSize Size
|
||||
{
|
||||
get => GetValue(SizeProperty);
|
||||
set => SetValue(SizeProperty, value);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get => GetValue(CountProperty);
|
||||
set => SetValue(CountProperty, value);
|
||||
}
|
||||
|
||||
public IBrush? BadgeShadowColor
|
||||
{
|
||||
get => GetValue(BadgeShadowColorProperty);
|
||||
set => SetValue(BadgeShadowColorProperty, value);
|
||||
}
|
||||
|
||||
public double BadgeShadowSize
|
||||
{
|
||||
get => GetValue(BadgeShadowSizeProperty);
|
||||
set => SetValue(BadgeShadowSizeProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 内部属性定义
|
||||
|
||||
internal static readonly StyledProperty<IBrush?> BadgeTextColorProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, IBrush?>(
|
||||
nameof(BadgeTextColor));
|
||||
|
||||
internal static readonly StyledProperty<double> TextFontSizeProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, double>(
|
||||
nameof(TextFontSize));
|
||||
|
||||
internal static readonly StyledProperty<double> IndicatorHeightProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, double>(
|
||||
nameof(IndicatorHeight));
|
||||
|
||||
internal static readonly StyledProperty<int> CountProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, int>(
|
||||
nameof(Count));
|
||||
|
||||
internal static readonly StyledProperty<FontFamily> FontFamilyProperty =
|
||||
TextElement.FontFamilyProperty.AddOwner<CountBadgeAdorner>();
|
||||
|
||||
internal static readonly StyledProperty<FontWeight> TextFontWeightProperty =
|
||||
TextElement.FontWeightProperty.AddOwner<CountBadgeAdorner>();
|
||||
|
||||
internal static readonly StyledProperty<IBrush?> BadgeShadowColorProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, IBrush?>(
|
||||
nameof(BadgeShadowColor));
|
||||
|
||||
internal static readonly StyledProperty<double> BadgeShadowSizeProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, double>(
|
||||
nameof(BadgeShadowSize));
|
||||
|
||||
internal static readonly StyledProperty<double> PaddingInlineProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, double>(
|
||||
nameof(PaddingInline));
|
||||
|
||||
internal static readonly DirectProperty<DotBadgeAdorner, bool> IsAdornerModeProperty =
|
||||
AvaloniaProperty.RegisterDirect<DotBadgeAdorner, bool>(
|
||||
internal static readonly DirectProperty<CountBadgeAdorner, bool> IsAdornerModeProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, bool>(
|
||||
nameof(IsAdornerMode),
|
||||
o => o.IsAdornerMode,
|
||||
(o, v) => o.IsAdornerMode = v);
|
||||
|
||||
internal static readonly StyledProperty<Point> OffsetProperty =
|
||||
AvaloniaProperty.Register<CountBadgeAdorner, Point>(
|
||||
nameof(Offset));
|
||||
internal static readonly DirectProperty<CountBadgeAdorner, BoxShadows> BoxShadowProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, BoxShadows>(
|
||||
nameof(BoxShadow),
|
||||
o => o.BoxShadow,
|
||||
(o, v) => o.BoxShadow = v);
|
||||
|
||||
internal IBrush? BadgeTextColor
|
||||
{
|
||||
get => GetValue(BadgeTextColorProperty);
|
||||
set => SetValue(BadgeTextColorProperty, value);
|
||||
}
|
||||
internal static readonly DirectProperty<CountBadgeAdorner, string?> CountTextProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, string?>(
|
||||
nameof(CountText),
|
||||
o => o.CountText,
|
||||
(o, v) => o.CountText = v);
|
||||
|
||||
internal double TextFontSize
|
||||
{
|
||||
get => GetValue(TextFontSizeProperty);
|
||||
set => SetValue(TextFontSizeProperty, value);
|
||||
}
|
||||
|
||||
internal double IndicatorHeight
|
||||
{
|
||||
get => GetValue(IndicatorHeightProperty);
|
||||
set => SetValue(IndicatorHeightProperty, value);
|
||||
}
|
||||
|
||||
internal int Count
|
||||
{
|
||||
get => GetValue(CountProperty);
|
||||
set => SetValue(CountProperty, value);
|
||||
}
|
||||
|
||||
internal FontFamily FontFamily
|
||||
{
|
||||
get => GetValue(FontFamilyProperty);
|
||||
set => SetValue(FontFamilyProperty, value);
|
||||
}
|
||||
|
||||
internal FontWeight TextFontWeight
|
||||
{
|
||||
get => GetValue(TextFontWeightProperty);
|
||||
set => SetValue(TextFontWeightProperty, value);
|
||||
}
|
||||
|
||||
internal IBrush? BadgeShadowColor
|
||||
{
|
||||
get => GetValue(BadgeShadowColorProperty);
|
||||
set => SetValue(BadgeShadowColorProperty, value);
|
||||
}
|
||||
|
||||
internal double BadgeShadowSize
|
||||
{
|
||||
get => GetValue(BadgeShadowSizeProperty);
|
||||
set => SetValue(BadgeShadowSizeProperty, value);
|
||||
}
|
||||
|
||||
internal double PaddingInline
|
||||
{
|
||||
get => GetValue(PaddingInlineProperty);
|
||||
set => SetValue(PaddingInlineProperty, value);
|
||||
}
|
||||
internal static readonly DirectProperty<CountBadgeAdorner, TimeSpan> MotionDurationProperty =
|
||||
AvaloniaProperty.RegisterDirect<CountBadgeAdorner, TimeSpan>(
|
||||
nameof(MotionDuration),
|
||||
o => o.MotionDuration,
|
||||
(o, v) => o.MotionDuration = v);
|
||||
|
||||
private bool _isAdornerMode;
|
||||
|
||||
@ -162,19 +119,33 @@ internal class CountBadgeAdorner : Control
|
||||
set => SetAndRaise(IsAdornerModeProperty, ref _isAdornerMode, value);
|
||||
}
|
||||
|
||||
private CountBadgeSize _size;
|
||||
|
||||
public CountBadgeSize Size
|
||||
private BoxShadows _boxShadow;
|
||||
public BoxShadows BoxShadow
|
||||
{
|
||||
get => _size;
|
||||
set => SetAndRaise(SizeProperty, ref _size, value);
|
||||
get => _boxShadow;
|
||||
set => SetAndRaise(BoxShadowProperty, ref _boxShadow, value);
|
||||
}
|
||||
|
||||
private string? _countText;
|
||||
public string? CountText
|
||||
{
|
||||
get => _countText;
|
||||
set => SetAndRaise(CountTextProperty, ref _countText, value);
|
||||
}
|
||||
|
||||
private TimeSpan _motionDuration;
|
||||
public TimeSpan MotionDuration
|
||||
{
|
||||
get => _motionDuration;
|
||||
set => SetAndRaise(MotionDurationProperty, ref _motionDuration, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// 不知道为什么这个值会被 AdornerLayer 重写
|
||||
// 非常不优美,但是能工作
|
||||
internal RelativePoint? AnimationRenderTransformOrigin;
|
||||
private Panel? _rootLayout;
|
||||
private MotionActorControl? _indicatorMotionActor;
|
||||
private CancellationTokenSource? _motionCancellationTokenSource;
|
||||
private bool _needInitialHide;
|
||||
|
||||
static CountBadgeAdorner()
|
||||
{
|
||||
@ -185,66 +156,28 @@ internal class CountBadgeAdorner : Control
|
||||
AffectsRender<CountBadgeAdorner>(BadgeColorProperty, OffsetProperty);
|
||||
}
|
||||
|
||||
private bool _initialized;
|
||||
private BoxShadows _boxShadows;
|
||||
private Size _countTextSize;
|
||||
private string? _countText;
|
||||
private readonly List<FormattedText> _formattedTexts;
|
||||
|
||||
public CountBadgeAdorner()
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
_formattedTexts = new List<FormattedText>();
|
||||
base.OnApplyTemplate(e);
|
||||
TokenResourceBinder.CreateTokenBinding(this, BadgeShadowSizeProperty, BadgeTokenResourceKey.BadgeShadowSize);
|
||||
TokenResourceBinder.CreateTokenBinding(this, BadgeShadowColorProperty, BadgeTokenResourceKey.BadgeShadowColor);
|
||||
TokenResourceBinder.CreateTokenBinding(this, MotionDurationProperty, GlobalTokenResourceKey.MotionDurationMid);
|
||||
TokenResourceBinder.CreateTokenBinding(this, BadgeColorProperty, BadgeTokenResourceKey.BadgeColor);
|
||||
_indicatorMotionActor = e.NameScope.Get<MotionActorControl>(CountBadgeAdornerTheme.IndicatorMotionActorPart);
|
||||
if (_needInitialHide)
|
||||
{
|
||||
_indicatorMotionActor.IsVisible = false;
|
||||
_needInitialHide = false;
|
||||
}
|
||||
|
||||
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnAttachedToLogicalTree(e);
|
||||
if (Styles.Count == 0)
|
||||
{
|
||||
BuildStyles();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildStyles()
|
||||
{
|
||||
var commonStyle = new Style();
|
||||
commonStyle.Add(TextFontWeightProperty, BadgeTokenResourceKey.TextFontWeight);
|
||||
commonStyle.Add(BadgeColorProperty, BadgeTokenResourceKey.BadgeColor);
|
||||
commonStyle.Add(BadgeShadowSizeProperty, BadgeTokenResourceKey.BadgeShadowSize);
|
||||
commonStyle.Add(BadgeShadowColorProperty, BadgeTokenResourceKey.BadgeShadowColor);
|
||||
commonStyle.Add(BadgeTextColorProperty, BadgeTokenResourceKey.BadgeTextColor);
|
||||
commonStyle.Add(PaddingInlineProperty, GlobalTokenResourceKey.PaddingXS);
|
||||
Styles.Add(commonStyle);
|
||||
|
||||
var defaultSizeStyle =
|
||||
new Style(selector => selector.PropertyEquals(SizeProperty, CountBadgeSize.Default));
|
||||
defaultSizeStyle.Add(TextFontSizeProperty, BadgeTokenResourceKey.TextFontSize);
|
||||
defaultSizeStyle.Add(IndicatorHeightProperty, BadgeTokenResourceKey.IndicatorHeight);
|
||||
Styles.Add(defaultSizeStyle);
|
||||
|
||||
var smallSizeStyle = new Style(selector => selector.PropertyEquals(SizeProperty, CountBadgeSize.Small));
|
||||
smallSizeStyle.Add(TextFontSizeProperty, BadgeTokenResourceKey.TextFontSizeSM);
|
||||
smallSizeStyle.Add(IndicatorHeightProperty, BadgeTokenResourceKey.IndicatorHeightSM);
|
||||
Styles.Add(smallSizeStyle);
|
||||
}
|
||||
|
||||
public override void ApplyTemplate()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
BuildBoxShadow();
|
||||
BuildCountText();
|
||||
CalculateCountTextSize();
|
||||
BuildFormattedTexts();
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildBoxShadow()
|
||||
{
|
||||
if (BadgeShadowColor is not null)
|
||||
{
|
||||
_boxShadows = new BoxShadows(new BoxShadow
|
||||
BoxShadow = new BoxShadows(new BoxShadow
|
||||
{
|
||||
OffsetX = 0,
|
||||
OffsetY = 0,
|
||||
@ -269,143 +202,100 @@ internal class CountBadgeAdorner : Control
|
||||
if (e.Property == CountProperty || e.Property == OverflowCountProperty)
|
||||
{
|
||||
BuildCountText();
|
||||
CalculateCountTextSize(true);
|
||||
BuildFormattedTexts(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
if (IsAdornerMode)
|
||||
{
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
return GetBadgePillSize();
|
||||
}
|
||||
|
||||
protected Size GetBadgePillSize()
|
||||
{
|
||||
var targetWidth = IndicatorHeight;
|
||||
var targetHeight = IndicatorHeight;
|
||||
if (_countText?.Length > 1)
|
||||
{
|
||||
targetWidth += PaddingInline;
|
||||
if (Count > _overflowCount)
|
||||
{
|
||||
targetWidth += PaddingInline;
|
||||
}
|
||||
}
|
||||
|
||||
targetWidth = Math.Max(targetWidth, _countTextSize.Width);
|
||||
targetHeight = Math.Max(targetHeight, _countTextSize.Height);
|
||||
return new Size(targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
private void CalculateCountTextSize(bool force = false)
|
||||
{
|
||||
if (force || _countTextSize == default)
|
||||
{
|
||||
var fontSize = TextFontSize;
|
||||
var typeface = new Typeface(FontFamily, FontStyle.Normal, TextFontWeight);
|
||||
var textLayout = new TextLayout(_countText,
|
||||
typeface,
|
||||
null,
|
||||
fontSize,
|
||||
null,
|
||||
lineHeight: IndicatorHeight);
|
||||
_countTextSize = new Size(Math.Round(textLayout.Width), Math.Round(textLayout.Height));
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildCountText()
|
||||
{
|
||||
if (Count > _overflowCount)
|
||||
CountText = Count > OverflowCount ? $"{OverflowCount}+" : $"{Count}";
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
_countText = $"{_overflowCount}+";
|
||||
var size = base.ArrangeOverride(finalSize);
|
||||
if (IsAdornerMode && _indicatorMotionActor is not null)
|
||||
{
|
||||
var offsetX = Offset.X;
|
||||
var offsetY = Offset.Y;
|
||||
var indicatorSize = _indicatorMotionActor.DesiredSize;
|
||||
offsetX += finalSize.Width - indicatorSize.Width / 2;
|
||||
offsetY -= indicatorSize.Height / 2;
|
||||
_indicatorMotionActor.Arrange(new Rect(new Point(offsetX, offsetY), indicatorSize));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private void ApplyShowMotion()
|
||||
{
|
||||
if (_indicatorMotionActor is not null)
|
||||
{
|
||||
_indicatorMotionActor.IsVisible = false;
|
||||
var zoomBadgeInMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeInMotion(MotionDuration, null,
|
||||
FillMode.Forward);
|
||||
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeInMotionConfig, () =>
|
||||
{
|
||||
_indicatorMotionActor.IsVisible = true;
|
||||
}, null, _motionCancellationTokenSource!.Token);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyHideMotion(Action completedAction)
|
||||
{
|
||||
if (_indicatorMotionActor is not null)
|
||||
{
|
||||
var zoomBadgeOutMotionConfig = BadgeMotionFactory.BuildBadgeZoomBadgeOutMotion(MotionDuration, null,
|
||||
FillMode.Forward);
|
||||
_motionCancellationTokenSource?.Cancel();
|
||||
_motionCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
MotionInvoker.Invoke(_indicatorMotionActor, zoomBadgeOutMotionConfig, null, () =>
|
||||
{
|
||||
completedAction();
|
||||
}, _motionCancellationTokenSource.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
_countText = $"{Count}";
|
||||
_needInitialHide = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
internal void ApplyToTarget(AdornerLayer? adornerLayer, Control adorned)
|
||||
{
|
||||
var offsetX = 0d;
|
||||
var offsetY = 0d;
|
||||
var badgeSize = GetBadgePillSize();
|
||||
if (IsAdornerMode)
|
||||
if (adornerLayer is not null)
|
||||
{
|
||||
offsetX = DesiredSize.Width - badgeSize.Width / 2;
|
||||
offsetY = -badgeSize.Height / 2;
|
||||
offsetX += Offset.X;
|
||||
offsetY += Offset.Y;
|
||||
adornerLayer.Children.Remove(this);
|
||||
|
||||
AdornerLayer.SetAdornedElement(this, adorned);
|
||||
AdornerLayer.SetIsClipEnabled(this, false);
|
||||
adornerLayer.Children.Add(this);
|
||||
}
|
||||
|
||||
var badgeRect = new Rect(new Point(offsetX, offsetY), badgeSize);
|
||||
_motionCancellationTokenSource?.Cancel();
|
||||
_motionCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (RenderTransform is not null)
|
||||
ApplyShowMotion();
|
||||
}
|
||||
|
||||
internal void DetachFromTarget(AdornerLayer? adornerLayer, bool enableMotion = true)
|
||||
{
|
||||
Point origin;
|
||||
if (AnimationRenderTransformOrigin.HasValue)
|
||||
|
||||
if (enableMotion)
|
||||
{
|
||||
origin = AnimationRenderTransformOrigin.Value.ToPixels(badgeRect.Size);
|
||||
ApplyHideMotion(() =>
|
||||
{
|
||||
if (adornerLayer is not null)
|
||||
{
|
||||
adornerLayer.Children.Remove(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = RenderTransformOrigin.ToPixels(badgeRect.Size);
|
||||
}
|
||||
|
||||
var offset = Matrix.CreateTranslation(new Point(origin.X + offsetX, origin.Y + offsetY));
|
||||
var renderTransform = -offset * RenderTransform.Value * offset;
|
||||
context.PushTransform(renderTransform);
|
||||
}
|
||||
|
||||
context.DrawPilledRect(BadgeColor, null, badgeRect, Orientation.Horizontal, _boxShadows);
|
||||
// 计算合适的文字 x 坐标
|
||||
var textOffsetX = offsetX + (badgeSize.Width - _countTextSize.Width) / 2;
|
||||
var textOffsetY = offsetY + (badgeSize.Height - _countTextSize.Height) / 2;
|
||||
foreach (var formattedText in _formattedTexts)
|
||||
if (adornerLayer is not null)
|
||||
{
|
||||
context.DrawText(formattedText, new Point(textOffsetX, textOffsetY));
|
||||
textOffsetX += formattedText.Width;
|
||||
adornerLayer.Children.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildFormattedTexts(bool force = false)
|
||||
{
|
||||
if (_formattedTexts.Count == 0 || force)
|
||||
{
|
||||
_formattedTexts.Clear();
|
||||
if (_countText is not null)
|
||||
{
|
||||
if (Count > _overflowCount)
|
||||
{
|
||||
// 生成一个即可
|
||||
_formattedTexts.Add(BuildFormattedText(_countText));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有数字都生成一个
|
||||
foreach (var c in _countText)
|
||||
{
|
||||
_formattedTexts.Add(BuildFormattedText(c.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FormattedText BuildFormattedText(string text)
|
||||
{
|
||||
var typeface = new Typeface(FontFamily, FontStyle.Normal, TextFontWeight);
|
||||
var formattedText = new FormattedText(text, CultureInfo.CurrentUICulture, GetFlowDirection(this),
|
||||
typeface, 1, BadgeTextColor);
|
||||
formattedText.SetFontSize(TextFontSize);
|
||||
formattedText.TextAlignment = TextAlignment.Left;
|
||||
formattedText.LineHeight = IndicatorHeight;
|
||||
return formattedText;
|
||||
}
|
||||
}
|
@ -1,13 +1,114 @@
|
||||
using AtomUI.Theme;
|
||||
using AtomUI.MotionScene;
|
||||
using AtomUI.Theme;
|
||||
using AtomUI.Theme.Styling;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace AtomUI.Controls.Badge;
|
||||
|
||||
[ControlThemeProvider]
|
||||
internal class CountBadgeAdornerTheme : BaseControlTheme
|
||||
{
|
||||
internal const string IndicatorMotionActorPart = "PART_IndicatorMotionActor";
|
||||
internal const string RootLayoutPart = "PART_RootLayout";
|
||||
internal const string BadgeIndicatorPart = "PART_BadgeIndicator";
|
||||
internal const string BadgeTextPart = "PART_BadgeText";
|
||||
|
||||
public CountBadgeAdornerTheme()
|
||||
: base(typeof(CountBadgeAdorner))
|
||||
{
|
||||
}
|
||||
|
||||
protected override IControlTemplate BuildControlTemplate()
|
||||
{
|
||||
return new FuncControlTemplate<CountBadgeAdorner>((adorner, scope) =>
|
||||
{
|
||||
var indicatorMotionActor = new MotionActorControl()
|
||||
{
|
||||
Name = IndicatorMotionActorPart,
|
||||
ClipToBounds = false,
|
||||
UseRenderTransform = true
|
||||
};
|
||||
var layout = new Panel()
|
||||
{
|
||||
Name = RootLayoutPart,
|
||||
};
|
||||
indicatorMotionActor.Child = layout;
|
||||
indicatorMotionActor.RegisterInNameScope(scope);
|
||||
layout.RegisterInNameScope(scope);
|
||||
BuildBadgeIndicator(adorner, layout, scope);
|
||||
BuildBadgeText(layout, scope);
|
||||
return indicatorMotionActor;
|
||||
});
|
||||
}
|
||||
|
||||
private void BuildBadgeIndicator(CountBadgeAdorner adorner, Panel layout, INameScope scope)
|
||||
{
|
||||
var indicator = new Border()
|
||||
{
|
||||
Name = BadgeIndicatorPart
|
||||
};
|
||||
|
||||
CreateTemplateParentBinding(indicator, Border.BoxShadowProperty, CountBadgeAdorner.BoxShadowProperty);
|
||||
CreateTemplateParentBinding(indicator, Border.BackgroundProperty, CountBadgeAdorner.BadgeColorProperty);
|
||||
|
||||
layout.Children.Add(indicator);
|
||||
indicator.RegisterInNameScope(scope);
|
||||
}
|
||||
|
||||
private void BuildBadgeText(Panel layout, INameScope scope)
|
||||
{
|
||||
var badgeText = new TextBlock()
|
||||
{
|
||||
Name = BadgeTextPart,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
CreateTemplateParentBinding(badgeText, TextBlock.TextProperty, CountBadgeAdorner.CountTextProperty);
|
||||
layout.Children.Add(badgeText);
|
||||
badgeText.RegisterInNameScope(scope);
|
||||
}
|
||||
|
||||
protected override void BuildStyles()
|
||||
{
|
||||
var commonStyle = new Style(selector => selector.Nesting());
|
||||
commonStyle.Add(CountBadgeAdorner.ClipToBoundsProperty, false);
|
||||
var inAdornerStyle = new Style(selector => selector.Nesting().PropertyEquals(CountBadgeAdorner.IsAdornerModeProperty, true));
|
||||
var layoutStyle = new Style(selector => selector.Nesting().Template().Name(RootLayoutPart));
|
||||
layoutStyle.Add(Panel.HorizontalAlignmentProperty, HorizontalAlignment.Right);
|
||||
layoutStyle.Add(Panel.VerticalAlignmentProperty, VerticalAlignment.Top);
|
||||
inAdornerStyle.Add(layoutStyle);
|
||||
commonStyle.Add(inAdornerStyle);
|
||||
{
|
||||
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(BadgeIndicatorPart));
|
||||
indicatorStyle.Add(Border.HeightProperty, BadgeTokenResourceKey.IndicatorHeight);
|
||||
indicatorStyle.Add(Border.MinWidthProperty, BadgeTokenResourceKey.IndicatorHeight);
|
||||
indicatorStyle.Add(Border.CornerRadiusProperty, BadgeTokenResourceKey.CountBadgeCornerRadius);
|
||||
commonStyle.Add(indicatorStyle);
|
||||
|
||||
var badgeTextStyle = new Style(selector => selector.Nesting().Template().Name(BadgeTextPart));
|
||||
badgeTextStyle.Add(TextBlock.ForegroundProperty, BadgeTokenResourceKey.BadgeTextColor);
|
||||
badgeTextStyle.Add(TextBlock.FontSizeProperty, BadgeTokenResourceKey.TextFontSize);
|
||||
badgeTextStyle.Add(TextBlock.PaddingProperty, BadgeTokenResourceKey.CountBadgeTextPadding);
|
||||
commonStyle.Add(badgeTextStyle);
|
||||
}
|
||||
|
||||
var smallSizeStyle = new Style(selector => selector.Nesting().PropertyEquals(CountBadgeAdorner.SizeProperty, CountBadgeSize.Small));
|
||||
{
|
||||
var indicatorStyle = new Style(selector => selector.Nesting().Template().Name(BadgeIndicatorPart));
|
||||
indicatorStyle.Add(Border.HeightProperty, BadgeTokenResourceKey.IndicatorHeightSM);
|
||||
indicatorStyle.Add(Border.MinWidthProperty, BadgeTokenResourceKey.IndicatorHeightSM);
|
||||
indicatorStyle.Add(Border.CornerRadiusProperty, BadgeTokenResourceKey.CountBadgeCornerRadiusSM);
|
||||
smallSizeStyle.Add(indicatorStyle);
|
||||
|
||||
var badgeTextStyle = new Style(selector => selector.Nesting().Template().Name(BadgeTextPart));
|
||||
badgeTextStyle.Add(TextBlock.FontSizeProperty, BadgeTokenResourceKey.TextFontSizeSM);
|
||||
smallSizeStyle.Add(badgeTextStyle);
|
||||
}
|
||||
commonStyle.Add(smallSizeStyle);
|
||||
|
||||
Add(commonStyle);
|
||||
}
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
using AtomUI.MotionScene;
|
||||
using Avalonia;
|
||||
using Avalonia.Animation.Easings;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace AtomUI.Controls.Badge;
|
||||
|
||||
internal class CountBadgeZoomBadgeIn : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
public MotionConfig? RenderTransformConfig => GetMotionConfig(MotionRenderTransformProperty);
|
||||
|
||||
public CountBadgeZoomBadgeIn()
|
||||
{
|
||||
MotionRenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
|
||||
}
|
||||
|
||||
public void ConfigureOpacity(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new ExponentialEaseOut();
|
||||
var config = new MotionConfig(MotionOpacityProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.Double,
|
||||
StartValue = 0d,
|
||||
EndValue = 1d,
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
public void ConfigureRenderTransform(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new BackEaseOut();
|
||||
|
||||
var config = new MotionConfig(MotionRenderTransformProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.TransformOperations,
|
||||
StartValue = BuildScaleTransform(0),
|
||||
EndValue = BuildScaleTransform(1),
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
internal override void NotifyConfigMotionTarget(Control motionTarget)
|
||||
{
|
||||
base.NotifyConfigMotionTarget(motionTarget);
|
||||
motionTarget.RenderTransformOrigin = MotionRenderTransformOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CountBadgeZoomBadgeOut : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
public MotionConfig? RenderTransformConfig => GetMotionConfig(MotionRenderTransformProperty);
|
||||
|
||||
public CountBadgeZoomBadgeOut()
|
||||
{
|
||||
MotionRenderTransformOrigin = new RelativePoint(0.5, 0.5, RelativeUnit.Relative);
|
||||
}
|
||||
|
||||
public void ConfigureOpacity(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new ExponentialEaseIn();
|
||||
var config = new MotionConfig(MotionOpacityProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.Double,
|
||||
StartValue = 1d,
|
||||
EndValue = 0d,
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
public void ConfigureRenderTransform(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new ExponentialEaseIn();
|
||||
|
||||
var config = new MotionConfig(MotionRenderTransformProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.TransformOperations,
|
||||
StartValue = BuildScaleTransform(1),
|
||||
EndValue = BuildScaleTransform(0),
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
internal override void NotifyConfigMotionTarget(Control motionTarget)
|
||||
{
|
||||
base.NotifyConfigMotionTarget(motionTarget);
|
||||
motionTarget.RenderTransformOrigin = MotionRenderTransformOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
internal class CountBadgeNoWrapperZoomBadgeIn : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
public MotionConfig? RenderTransformConfig => GetMotionConfig(MotionRenderTransformProperty);
|
||||
|
||||
public void ConfigureOpacity(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new QuarticEaseOut();
|
||||
var config = new MotionConfig(MotionOpacityProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.Double,
|
||||
StartValue = 0d,
|
||||
EndValue = 1d,
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
public void ConfigureRenderTransform(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new QuarticEaseOut();
|
||||
|
||||
var config = new MotionConfig(MotionRenderTransformProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.TransformOperations,
|
||||
StartValue = BuildScaleTransform(0),
|
||||
EndValue = BuildScaleTransform(1),
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
internal class CountBadgeNoWrapperZoomBadgeOut : AbstractMotion
|
||||
{
|
||||
public MotionConfig? OpacityConfig => GetMotionConfig(MotionOpacityProperty);
|
||||
public MotionConfig? RenderTransformConfig => GetMotionConfig(MotionRenderTransformProperty);
|
||||
|
||||
public void ConfigureOpacity(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new CircularEaseIn();
|
||||
var config = new MotionConfig(MotionOpacityProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.Double,
|
||||
StartValue = 1d,
|
||||
EndValue = 0d,
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
|
||||
public void ConfigureRenderTransform(TimeSpan duration, Easing? easing = null)
|
||||
{
|
||||
easing ??= new CircularEaseIn();
|
||||
|
||||
var config = new MotionConfig(MotionRenderTransformProperty)
|
||||
{
|
||||
TransitionKind = TransitionKind.TransformOperations,
|
||||
StartValue = BuildScaleTransform(1),
|
||||
EndValue = BuildScaleTransform(0),
|
||||
MotionDuration = duration,
|
||||
MotionEasing = easing
|
||||
};
|
||||
AddMotionConfig(config);
|
||||
}
|
||||
}
|
@ -125,20 +125,20 @@ public class DotBadge : Control
|
||||
}
|
||||
}
|
||||
|
||||
private void HideAdorner()
|
||||
private void HideAdorner(bool enableMotion)
|
||||
{
|
||||
// 这里需要抛出异常吗?
|
||||
if ( _dotBadgeAdorner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_dotBadgeAdorner.DetachFromTarget(_adornerLayer);
|
||||
_dotBadgeAdorner.DetachFromTarget(_adornerLayer, enableMotion);
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
HideAdorner();
|
||||
HideAdorner(false);
|
||||
}
|
||||
|
||||
private void SetupTokenBindings()
|
||||
@ -186,32 +186,17 @@ public class DotBadge : Control
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (e.Property == IsVisibleProperty)
|
||||
if (e.Property == IsVisibleProperty ||
|
||||
e.Property == BadgeIsVisibleProperty)
|
||||
{
|
||||
var badgeIsVisible = e.GetNewValue<bool>();
|
||||
if (badgeIsVisible)
|
||||
{
|
||||
if (_adornerLayer is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideAdorner();
|
||||
}
|
||||
}
|
||||
else if (e.Property == BadgeIsVisibleProperty)
|
||||
{
|
||||
if (BadgeIsVisible)
|
||||
{
|
||||
PrepareAdorner();
|
||||
}
|
||||
else
|
||||
{
|
||||
HideAdorner();
|
||||
HideAdorner(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,8 +187,8 @@ internal class DotBadgeAdorner : TemplatedControl
|
||||
var offsetY = Offset.Y;
|
||||
var dotSize = _indicatorMotionActor.Bounds.Size;
|
||||
offsetX += dotSize.Width / 3;
|
||||
offsetY += dotSize.Height / 3;
|
||||
_indicatorMotionActor.Arrange(new Rect(new Point(offsetX, -offsetY), dotSize));
|
||||
offsetY -= dotSize.Height / 3;
|
||||
_indicatorMotionActor.Arrange(new Rect(new Point(offsetX, offsetY), dotSize));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
@ -212,12 +212,20 @@ internal class DotBadgeAdorner : TemplatedControl
|
||||
ApplyShowMotion();
|
||||
}
|
||||
|
||||
internal void DetachFromTarget(AdornerLayer? adornerLayer)
|
||||
internal void DetachFromTarget(AdornerLayer? adornerLayer, bool enableMotion = true)
|
||||
{
|
||||
if (adornerLayer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (enableMotion)
|
||||
{
|
||||
ApplyHideMotion(() => adornerLayer.Children.Remove(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
adornerLayer.Children.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ internal class DotBadgeAdornerTheme : BaseControlTheme
|
||||
{
|
||||
}
|
||||
|
||||
protected override IControlTemplate? BuildControlTemplate()
|
||||
protected override IControlTemplate BuildControlTemplate()
|
||||
{
|
||||
return new FuncControlTemplate<DotBadgeAdorner>((adorner, scope) =>
|
||||
{
|
||||
|
@ -65,6 +65,9 @@ namespace AtomUI.Theme.Styling
|
||||
public static readonly TokenResourceKey BadgeRibbonCornerDarkenAmount = new TokenResourceKey("Badge.BadgeRibbonCornerDarkenAmount", "AtomUI.Token");
|
||||
public static readonly TokenResourceKey BadgeRibbonTextPadding = new TokenResourceKey("Badge.BadgeRibbonTextPadding", "AtomUI.Token");
|
||||
public static readonly TokenResourceKey DotBadgeLabelMargin = new TokenResourceKey("Badge.DotBadgeLabelMargin", "AtomUI.Token");
|
||||
public static readonly TokenResourceKey CountBadgeTextPadding = new TokenResourceKey("Badge.CountBadgeTextPadding", "AtomUI.Token");
|
||||
public static readonly TokenResourceKey CountBadgeCornerRadius = new TokenResourceKey("Badge.CountBadgeCornerRadius", "AtomUI.Token");
|
||||
public static readonly TokenResourceKey CountBadgeCornerRadiusSM = new TokenResourceKey("Badge.CountBadgeCornerRadiusSM", "AtomUI.Token");
|
||||
}
|
||||
|
||||
public static class ButtonSpinnerTokenResourceKey
|
||||
|
Loading…
Reference in New Issue
Block a user