diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml
index 72763528b..17a194f9e 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml
@@ -16,5 +16,76 @@
Show Notification
+
+
+
+ Open the notification box
+
+
+
+
+
+
+ Success
+
+
+ Info
+
+
+ Warning
+
+
+ Error
+
+
+
+
+
+
+
+
+ Top
+
+
+ Bottom
+
+
+
+
+
+ TopLeft
+
+
+ TopRight
+
+
+
+
+
+ BottomLeft
+
+
+ BottomRight
+
+
+
+
\ No newline at end of file
diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml.cs b/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml.cs
index b3e37bc57..ba7f8f840 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml.cs
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/NotificationShowCase.axaml.cs
@@ -7,7 +7,15 @@ namespace AtomUI.Demo.Desktop.ShowCase;
public partial class NotificationShowCase : UserControl
{
- private WindowNotificationManager? _windowNotificationManager;
+ private WindowNotificationManager? _basicManager;
+ private WindowNotificationManager? _topLeftManager;
+ private WindowNotificationManager? _topManager;
+ private WindowNotificationManager? _topRightManager;
+
+ private WindowNotificationManager? _bottomLeftManager;
+ private WindowNotificationManager? _bottomManager;
+ private WindowNotificationManager? _bottomRightManager;
+
public NotificationShowCase()
{
InitializeComponent();
@@ -17,20 +25,159 @@ public partial class NotificationShowCase : UserControl
{
base.OnAttachedToVisualTree(e);
var topLevel = TopLevel.GetTopLevel(this);
- _windowNotificationManager = new WindowNotificationManager(topLevel)
+ _basicManager = new WindowNotificationManager(topLevel)
{
- MaxItems = 5,
- IsPauseOnHover = true
+ MaxItems = 3
+ };
+
+ _topLeftManager = new WindowNotificationManager(topLevel)
+ {
+ MaxItems = 3,
+ Position = NotificationPosition.TopLeft
+ };
+
+ _topManager = new WindowNotificationManager(topLevel)
+ {
+ Position = NotificationPosition.TopCenter,
+ MaxItems = 3
+ };
+
+ _topRightManager = new WindowNotificationManager(topLevel)
+ {
+ Position = NotificationPosition.TopRight,
+ MaxItems = 3
+ };
+
+ _bottomLeftManager = new WindowNotificationManager(topLevel)
+ {
+ Position = NotificationPosition.BottomLeft,
+ MaxItems = 3
+ };
+
+ _bottomManager = new WindowNotificationManager(topLevel)
+ {
+ Position = NotificationPosition.BottomCenter,
+ MaxItems = 3
+ };
+
+ _bottomRightManager = new WindowNotificationManager(topLevel)
+ {
+ Position = NotificationPosition.BottomRight,
+ MaxItems = 3
};
}
private void ShowSimpleNotification(object? sender, RoutedEventArgs e)
{
- _windowNotificationManager?.Show(new Notification()
+ _basicManager?.Show(new Notification()
{
ShowProgress = true,
Title = "Notification Title",
- Content = "This is the content of the notification. This is the content of the notification. This is the content of the notification. This is the content of the notification."
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowNeverCloseNotification(object? sender, RoutedEventArgs e)
+ {
+ _basicManager?.Show(new Notification()
+ {
+ Expiration = TimeSpan.Zero,
+ Title = "Notification Title",
+ Content = "I will never close automatically. This is a purposely very very long description that has many many characters and words."
+ });
+ }
+
+ private void ShowSuccessNotification(object? sender, RoutedEventArgs e)
+ {
+ _basicManager?.Show(new Notification()
+ {
+ Type = NotificationType.Success,
+ Title = "Notification Title",
+ Content = "This is the content of the notification. This is the content of the notification. This is the content of the notification."
+ });
+ }
+
+ private void ShowInfoNotification(object? sender, RoutedEventArgs e)
+ {
+ _basicManager?.Show(new Notification()
+ {
+ Type = NotificationType.Information,
+ Title = "Notification Title",
+ Content = "This is the content of the notification. This is the content of the notification. This is the content of the notification."
+ });
+ }
+
+ private void ShowWarningNotification(object? sender, RoutedEventArgs e)
+ {
+ _basicManager?.Show(new Notification()
+ {
+ Type = NotificationType.Warning,
+ Title = "Notification Title",
+ Content = "This is the content of the notification. This is the content of the notification. This is the content of the notification."
+ });
+ }
+
+ private void ShowErrorNotification(object? sender, RoutedEventArgs e)
+ {
+ _basicManager?.Show(new Notification()
+ {
+ Type = NotificationType.Error,
+ Title = "Notification Title",
+ Content = "This is the content of the notification. This is the content of the notification. This is the content of the notification."
+ });
+ }
+
+ private void ShowTopNotification(object? sender, RoutedEventArgs e)
+ {
+ _topManager?.Show(new Notification()
+ {
+ Title = "Notification Top",
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowBottomNotification(object? sender, RoutedEventArgs e)
+ {
+ _bottomManager?.Show(new Notification()
+ {
+ Title = "Notification Bottom",
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowTopLeftNotification(object? sender, RoutedEventArgs e)
+ {
+ _topLeftManager?.Show(new Notification()
+ {
+ Title = "Notification TopLeft",
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowTopRightNotification(object? sender, RoutedEventArgs e)
+ {
+ _topRightManager?.Show(new Notification()
+ {
+ Title = "Notification TopRight",
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowBottomLeftNotification(object? sender, RoutedEventArgs e)
+ {
+ _bottomLeftManager?.Show(new Notification()
+ {
+ Title = "Notification BottomLeft",
+ Content = "Hello, AtomUI/Avalonia!"
+ });
+ }
+
+ private void ShowBottomRightNotification(object? sender, RoutedEventArgs e)
+ {
+ _bottomRightManager?.Show(new Notification()
+ {
+ Title = "Notification BottomRight",
+ Content = "Hello, AtomUI/Avalonia!"
});
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
index 58d87f391..e35fefbf7 100644
--- a/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
+++ b/src/AtomUI.Controls/GeneratedFiles/AtomUI.Generator/AtomUI.Generator.TokenResourceKeyGenerator/TokenResourceConst.g.cs
@@ -314,7 +314,8 @@ namespace AtomUI.Theme.Styling
public static readonly TokenResourceKey NotificationIconMargin = new TokenResourceKey("Notification.NotificationIconMargin", "AtomUI.Token");
public static readonly TokenResourceKey NotificationCloseButtonSize = new TokenResourceKey("Notification.NotificationCloseButtonSize", "AtomUI.Token");
public static readonly TokenResourceKey NotificationMarginBottom = new TokenResourceKey("Notification.NotificationMarginBottom", "AtomUI.Token");
- public static readonly TokenResourceKey NotificationMarginEdge = new TokenResourceKey("Notification.NotificationMarginEdge", "AtomUI.Token");
+ public static readonly TokenResourceKey NotificationTopMargin = new TokenResourceKey("Notification.NotificationTopMargin", "AtomUI.Token");
+ public static readonly TokenResourceKey NotificationBottomMargin = new TokenResourceKey("Notification.NotificationBottomMargin", "AtomUI.Token");
public static readonly TokenResourceKey NotificationProgressBg = new TokenResourceKey("Notification.NotificationProgressBg", "AtomUI.Token");
public static readonly TokenResourceKey NotificationProgressHeight = new TokenResourceKey("Notification.NotificationProgressHeight", "AtomUI.Token");
public static readonly TokenResourceKey NotificationProgressMargin = new TokenResourceKey("Notification.NotificationProgressMargin", "AtomUI.Token");
diff --git a/src/AtomUI.Controls/Notifications/NotificationCard.cs b/src/AtomUI.Controls/Notifications/NotificationCard.cs
index c2e43009b..d7a331e65 100644
--- a/src/AtomUI.Controls/Notifications/NotificationCard.cs
+++ b/src/AtomUI.Controls/Notifications/NotificationCard.cs
@@ -8,6 +8,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
+using Avalonia.LogicalTree;
namespace AtomUI.Controls;
@@ -132,7 +133,14 @@ public class NotificationCard : TemplatedControl
internal static readonly DirectProperty EffectiveShowProgressProperty =
AvaloniaProperty.RegisterDirect(nameof(EffectiveShowProgress),
- o => o.EffectiveShowProgress);
+ o => o.EffectiveShowProgress,
+ (o, v) => o.EffectiveShowProgress = v);
+
+ internal static readonly DirectProperty PositionProperty =
+ AvaloniaProperty.RegisterDirect(
+ nameof(Position),
+ o => o.Position,
+ (o, v) => o.Position = v);
private bool _effectiveShowProgress;
internal bool EffectiveShowProgress
@@ -141,6 +149,13 @@ public class NotificationCard : TemplatedControl
set => SetAndRaise(EffectiveShowProgressProperty, ref _effectiveShowProgress, value);
}
+ private NotificationPosition _position;
+ internal NotificationPosition Position
+ {
+ get => _position;
+ set => SetAndRaise(PositionProperty, ref _position, value);
+ }
+
#endregion
///
@@ -175,10 +190,15 @@ public class NotificationCard : TemplatedControl
IsClosing = true;
}
+ protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToLogicalTree(e);
+ UpdatePseudoClasses(Position);
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- SetupContent();
if (Icon is null) {
SetupNotificationIcon();
UpdateNotificationType();
@@ -232,6 +252,10 @@ public class NotificationCard : TemplatedControl
e.Property == IsClosedProperty) {
SetupEffectiveShowProgress();
}
+
+ if (e.Property == PositionProperty) {
+ UpdatePseudoClasses(e.GetNewValue());
+ }
}
private void SetupEffectiveShowProgress()
@@ -369,4 +393,14 @@ public class NotificationCard : TemplatedControl
_notificationManager.StartExpiredTimer();
}
}
+
+ private void UpdatePseudoClasses(NotificationPosition position)
+ {
+ PseudoClasses.Set(WindowNotificationManager.TopLeftPC, position == NotificationPosition.TopLeft);
+ PseudoClasses.Set(WindowNotificationManager.TopRightPC, position == NotificationPosition.TopRight);
+ PseudoClasses.Set(WindowNotificationManager.BottomLeftPC, position == NotificationPosition.BottomLeft);
+ PseudoClasses.Set(WindowNotificationManager.BottomRightPC, position == NotificationPosition.BottomRight);
+ PseudoClasses.Set(WindowNotificationManager.TopCenterPC, position == NotificationPosition.TopCenter);
+ PseudoClasses.Set(WindowNotificationManager.BottomCenterPC, position == NotificationPosition.BottomCenter);
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/Notifications/NotificationCardTheme.cs b/src/AtomUI.Controls/Notifications/NotificationCardTheme.cs
index 484fd11c1..89e615d45 100644
--- a/src/AtomUI.Controls/Notifications/NotificationCardTheme.cs
+++ b/src/AtomUI.Controls/Notifications/NotificationCardTheme.cs
@@ -27,6 +27,14 @@ internal class NotificationCardTheme : BaseControlTheme
public const string CloseButtonPart = "PART_CloseButton";
public const string LayoutTransformControlPart = "PART_LayoutTransformControl";
public const string ProgressBarPart = "PART_ProgressBar";
+ public const string MarginGhostDecoratorPart = "PART_MarginGhostDecorator";
+
+ public const double AnimationMaxOffsetY = 150d;
+ public const double AnimationMaxOffsetX = 500d;
+ public const int AnimationDuration = 400;
+
+ private Easing _quadraticEaseOut = new QuadraticEaseOut();
+ private Easing _quadraticEaseIn = new QuadraticEaseIn();
public NotificationCardTheme()
: base(typeof(NotificationCard))
@@ -44,11 +52,20 @@ internal class NotificationCardTheme : BaseControlTheme
ClipToBounds = false
};
layoutTransformControl.RegisterInNameScope(scope);
+
+ // 防止关闭的时候抖动,如果直接设置到 NotificationCard,layoutTransformControl没有办法平滑处理
+ var marginGhostDecorator = new Border()
+ {
+ Name = MarginGhostDecoratorPart
+ };
+
var frameDecorator = new Border()
{
Name = FrameDecoratorPart
};
+ marginGhostDecorator.Child = frameDecorator;
+
var mainLayout = new Grid()
{
ColumnDefinitions = new ColumnDefinitions()
@@ -70,7 +87,7 @@ internal class NotificationCardTheme : BaseControlTheme
BuildProgressBar(mainLayout, scope);
frameDecorator.RegisterInNameScope(scope);
- layoutTransformControl.Child = frameDecorator;
+ layoutTransformControl.Child = marginGhostDecorator;
return layoutTransformControl;
});
}
@@ -191,8 +208,56 @@ internal class NotificationCardTheme : BaseControlTheme
private void BuildCommonStyle()
{
var commonStyle = new Style(selector => selector.Nesting());
+
+ var topLeftStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopLeft));
+
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationTopMargin);
+ topLeftStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(topLeftStyle);
+
+ var topCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopCenter));
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationTopMargin);
+ topCenterStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(topCenterStyle);
+
+ var topRightStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopRight));
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationTopMargin);
+ topRightStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(topRightStyle);
+
+ var bottomLeftStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomLeft));
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationBottomMargin);
+ bottomLeftStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(bottomLeftStyle);
+
+ var bottomCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomCenter));
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationBottomMargin);
+ bottomCenterStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(bottomCenterStyle);
+
+ var bottomRightStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomRight));
+ {
+ var marginGhostDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(MarginGhostDecoratorPart));
+ marginGhostDecoratorStyle.Add(Border.MarginProperty, NotificationTokenResourceKey.NotificationBottomMargin);
+ bottomRightStyle.Add(marginGhostDecoratorStyle);
+ }
+ commonStyle.Add(bottomRightStyle);
- commonStyle.Add(NotificationCard.MarginProperty, NotificationTokenResourceKey.NotificationMarginEdge);
commonStyle.Add(NotificationCard.WidthProperty, NotificationTokenResourceKey.NotificationWidth);
var frameDecoratorStyle = new Style(selector => selector.Nesting().Template().Name(FrameDecoratorPart));
@@ -202,6 +267,10 @@ internal class NotificationCardTheme : BaseControlTheme
frameDecoratorStyle.Add(Border.CornerRadiusProperty, GlobalTokenResourceKey.BorderRadiusLG);
commonStyle.Add(frameDecoratorStyle);
+ var closedStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.IsClosedProperty, true));
+ closedStyle.Add(NotificationCard.MarginProperty, new Thickness(0));
+ commonStyle.Add(closedStyle);
+
Add(commonStyle);
}
@@ -239,23 +308,51 @@ internal class NotificationCardTheme : BaseControlTheme
{
var commonStyle = new Style(selector => selector.Nesting());
+ var topLeftStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopLeft));
+ BuildLeftEdgeAnimation(topLeftStyle);
+ commonStyle.Add(topLeftStyle);
+
+ var topCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopCenter));
+
+ commonStyle.Add(topCenterStyle);
+ BuildTopCenterEdgeAnimation(topCenterStyle);
+ var topRightStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.TopRight));
+ commonStyle.Add(topRightStyle);
+ BuildRightEdgeAnimation(topRightStyle);
+ var bottomLeftStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomLeft));
+ BuildLeftEdgeAnimation(bottomLeftStyle);
+ commonStyle.Add(bottomLeftStyle);
+
+ var bottomCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomCenter));
+ BuildBottomCenterEdgeAnimation(bottomCenterStyle);
+ commonStyle.Add(bottomCenterStyle);
+
+ var bottomRightStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.PositionProperty, NotificationPosition.BottomRight));
+ BuildRightEdgeAnimation(bottomRightStyle);
+ commonStyle.Add(bottomRightStyle);
+
+ Add(commonStyle);
+ }
+
+ private void BuildRightEdgeAnimation(Style parentStyle)
+ {
{
var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
- var moveRightInMotionConfig = MotionFactory.BuildMoveRightInMotion(400, TimeSpan.FromMilliseconds(400), new QuadraticEaseOut(),
+ var moveRightInMotionConfig = MotionFactory.BuildMoveRightInMotion(AnimationMaxOffsetX, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseOut,
FillMode.Forward);
foreach (var animation in moveRightInMotionConfig.Animations) {
layoutTransformStyle.Animations.Add(animation);
}
-
- commonStyle.Add(layoutTransformStyle);
+
+ parentStyle.Add(layoutTransformStyle);
}
-
+
var isClosingStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.IsClosingProperty, true));
{
var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
- var moveRightOutMotionConfig = MotionFactory.BuildMoveRightOutMotion(400, TimeSpan.FromMilliseconds(400), new QuadraticEaseIn(), FillMode.Forward);
+ var moveRightOutMotionConfig = MotionFactory.BuildMoveRightOutMotion(AnimationMaxOffsetX, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseIn, FillMode.Forward);
foreach (var animation in moveRightOutMotionConfig.Animations) {
layoutTransformStyle.Animations.Add(animation);
@@ -263,8 +360,53 @@ internal class NotificationCardTheme : BaseControlTheme
isClosingStyle.Animations.Add(new Animation()
{
- Duration = TimeSpan.FromMilliseconds(600),
- Easing = new QuadraticEaseIn(),
+ Duration = TimeSpan.FromMilliseconds(AnimationDuration * 1.2),
+ Easing = _quadraticEaseIn,
+ FillMode = FillMode.Forward,
+ Children =
+ {
+ new KeyFrame()
+ {
+ Cue = new Cue(1.0),
+ Setters =
+ {
+ new Setter(NotificationCard.IsClosedProperty, true)
+ }
+ }
+ }
+ });
+
+ isClosingStyle.Add(layoutTransformStyle);
+ }
+ parentStyle.Add(isClosingStyle);
+ }
+
+ private void BuildLeftEdgeAnimation(Style parentStyle)
+ {
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+ var moveLeftInMotionConfig = MotionFactory.BuildMoveLeftInMotion(AnimationMaxOffsetX, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseOut,
+ FillMode.Forward);
+ foreach (var animation in moveLeftInMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
+
+ parentStyle.Add(layoutTransformStyle);
+ }
+
+ var isClosingStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.IsClosingProperty, true));
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+ var moveRightOutMotionConfig = MotionFactory.BuildMoveLeftOutMotion(AnimationMaxOffsetX, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseIn, FillMode.Forward);
+
+ foreach (var animation in moveRightOutMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
+
+ isClosingStyle.Animations.Add(new Animation()
+ {
+ Duration = TimeSpan.FromMilliseconds(AnimationDuration * 1.2),
+ Easing = _quadraticEaseIn,
FillMode = FillMode.Forward,
Children =
{
@@ -274,7 +416,6 @@ internal class NotificationCardTheme : BaseControlTheme
Setters =
{
new Setter(NotificationCard.IsClosedProperty, true),
- new Setter(NotificationCard.MarginProperty, new Thickness(0)),
}
}
}
@@ -282,8 +423,99 @@ internal class NotificationCardTheme : BaseControlTheme
isClosingStyle.Add(layoutTransformStyle);
}
- commonStyle.Add(isClosingStyle);
+ parentStyle.Add(isClosingStyle);
+ }
+
+ private void BuildTopCenterEdgeAnimation(Style parentStyle)
+ {
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+ var moveRightInMotionConfig = MotionFactory.BuildMoveUpInMotion(AnimationMaxOffsetY, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseOut,
+ FillMode.Forward);
+ foreach (var animation in moveRightInMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
- Add(commonStyle);
+ parentStyle.Add(layoutTransformStyle);
+ }
+
+ var isClosingStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.IsClosingProperty, true));
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+
+ var moveRightOutMotionConfig = MotionFactory.BuildMoveUpOutMotion(AnimationMaxOffsetY, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseIn, FillMode.Forward);
+
+ foreach (var animation in moveRightOutMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
+
+ isClosingStyle.Animations.Add(new Animation()
+ {
+ Duration = TimeSpan.FromMilliseconds(AnimationDuration * 1.2),
+ Easing = _quadraticEaseIn,
+ FillMode = FillMode.Forward,
+ Children =
+ {
+ new KeyFrame()
+ {
+ Cue = new Cue(1.0),
+ Setters =
+ {
+ new Setter(NotificationCard.IsClosedProperty, true)
+ }
+ }
+ }
+ });
+
+ isClosingStyle.Add(layoutTransformStyle);
+ }
+ parentStyle.Add(isClosingStyle);
+ }
+
+
+ private void BuildBottomCenterEdgeAnimation(Style parentStyle)
+ {
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+ var moveRightInMotionConfig = MotionFactory.BuildMoveDownInMotion(AnimationMaxOffsetY, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseOut,
+ FillMode.Forward);
+ foreach (var animation in moveRightInMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
+
+ parentStyle.Add(layoutTransformStyle);
+ }
+
+ var isClosingStyle = new Style(selector => selector.Nesting().PropertyEquals(NotificationCard.IsClosingProperty, true));
+ {
+ var layoutTransformStyle = new Style(selector => selector.Nesting().Template().Name(LayoutTransformControlPart));
+
+ var moveRightOutMotionConfig = MotionFactory.BuildMoveDownOutMotion(AnimationMaxOffsetY, TimeSpan.FromMilliseconds(AnimationDuration), _quadraticEaseIn, FillMode.Forward);
+
+ foreach (var animation in moveRightOutMotionConfig.Animations) {
+ layoutTransformStyle.Animations.Add(animation);
+ }
+
+ isClosingStyle.Animations.Add(new Animation()
+ {
+ Duration = TimeSpan.FromMilliseconds(AnimationDuration * 1.2),
+ Easing = _quadraticEaseIn,
+ FillMode = FillMode.Forward,
+ Children =
+ {
+ new KeyFrame()
+ {
+ Cue = new Cue(1.0),
+ Setters =
+ {
+ new Setter(NotificationCard.IsClosedProperty, true)
+ }
+ }
+ }
+ });
+
+ isClosingStyle.Add(layoutTransformStyle);
+ }
+ parentStyle.Add(isClosingStyle);
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/Notifications/NotificationToken.cs b/src/AtomUI.Controls/Notifications/NotificationToken.cs
index f6c627818..d543ce5a4 100644
--- a/src/AtomUI.Controls/Notifications/NotificationToken.cs
+++ b/src/AtomUI.Controls/Notifications/NotificationToken.cs
@@ -50,9 +50,14 @@ internal class NotificationToken : AbstractControlDesignToken
public Thickness NotificationMarginBottom { get; set; }
///
- /// 提醒框边缘外边距
+ /// 提醒框上边缘外边距
///
- public Thickness NotificationMarginEdge { get; set; }
+ public Thickness NotificationTopMargin { get; set; }
+
+ ///
+ /// 提醒框下边缘外边距
+ ///
+ public Thickness NotificationBottomMargin { get; set; }
///
/// 提醒框进度条背景色
@@ -95,8 +100,8 @@ internal class NotificationToken : AbstractControlDesignToken
NotificationIconSize = _globalToken.FontToken.FontSizeLG * _globalToken.FontToken.LineHeightLG;
NotificationCloseButtonSize = _globalToken.HeightToken.ControlHeightLG * 0.55;
NotificationMarginBottom = new Thickness(0, 0, 0, _globalToken.Margin);
- NotificationMarginEdge = new Thickness(_globalToken.MarginLG, _globalToken.MarginLG, _globalToken.MarginLG, 0);
- AnimationMaxHeight = 150;
+ NotificationTopMargin = new Thickness(_globalToken.MarginLG, _globalToken.MarginLG, _globalToken.MarginLG, 0);
+ NotificationBottomMargin = new Thickness(_globalToken.MarginLG, 0, _globalToken.MarginLG, _globalToken.MarginLG);
NotificationProgressBg = new LinearGradientBrush()
{
diff --git a/src/AtomUI.Controls/Notifications/WindowNotificationManager.cs b/src/AtomUI.Controls/Notifications/WindowNotificationManager.cs
index 32c032a77..5c4f3fbf3 100644
--- a/src/AtomUI.Controls/Notifications/WindowNotificationManager.cs
+++ b/src/AtomUI.Controls/Notifications/WindowNotificationManager.cs
@@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Specialized;
+using AtomUI.Data;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
@@ -147,6 +148,7 @@ public class WindowNotificationManager : TemplatedControl, INotificationManager
Expiration = expiration == TimeSpan.Zero ? null : expiration,
IsShowProgress = notification.ShowProgress
};
+ BindUtils.RelayBind(this, PositionProperty, notificationControl, NotificationCard.PositionProperty);
// Add style classes if any
if (classes != null) {
@@ -162,13 +164,6 @@ public class WindowNotificationManager : TemplatedControl, INotificationManager
_items?.Remove(sender);
};
- notificationControl.PointerPressed += (sender, args) =>
- {
- onClick?.Invoke();
-
- (sender as NotificationCard)?.Close();
- };
-
Dispatcher.UIThread.Post(() =>
{
_items?.Add(notificationControl);
diff --git a/src/AtomUI.Controls/Notifications/WindowNotificationManagerTheme.cs b/src/AtomUI.Controls/Notifications/WindowNotificationManagerTheme.cs
index 2afa2a15d..2d416e7b3 100644
--- a/src/AtomUI.Controls/Notifications/WindowNotificationManagerTheme.cs
+++ b/src/AtomUI.Controls/Notifications/WindowNotificationManagerTheme.cs
@@ -34,6 +34,7 @@ internal class WindowNotificationManagerTheme : BaseControlTheme
var topLeftStyle = new Style(selector => selector.Nesting().Class(WindowNotificationManager.TopLeftPC));
{
var itemsStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPart));
+ itemsStyle.Add(ReversibleStackPanel.ReverseOrderProperty, true);
itemsStyle.Add(ReversibleStackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Left);
itemsStyle.Add(ReversibleStackPanel.VerticalAlignmentProperty, VerticalAlignment.Top);
topLeftStyle.Add(itemsStyle);
@@ -54,6 +55,7 @@ internal class WindowNotificationManagerTheme : BaseControlTheme
var topCenterStyle = new Style(selector => selector.Nesting().Class(WindowNotificationManager.TopCenterPC));
{
var itemsStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPart));
+ itemsStyle.Add(ReversibleStackPanel.ReverseOrderProperty, true);
itemsStyle.Add(ReversibleStackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Center);
itemsStyle.Add(ReversibleStackPanel.VerticalAlignmentProperty, VerticalAlignment.Top);
topCenterStyle.Add(itemsStyle);
@@ -65,7 +67,7 @@ internal class WindowNotificationManagerTheme : BaseControlTheme
var itemsStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPart));
itemsStyle.Add(ReversibleStackPanel.HorizontalAlignmentProperty, HorizontalAlignment.Left);
itemsStyle.Add(ReversibleStackPanel.VerticalAlignmentProperty, VerticalAlignment.Bottom);
- topLeftStyle.Add(itemsStyle);
+ bottomLeftStyle.Add(itemsStyle);
}
Add(bottomLeftStyle);
diff --git a/src/AtomUI.Controls/Utils/Motion/MoveMotionFactory.cs b/src/AtomUI.Controls/Utils/Motion/MoveMotionFactory.cs
index c6d4c6bcc..44e2a6620 100644
--- a/src/AtomUI.Controls/Utils/Motion/MoveMotionFactory.cs
+++ b/src/AtomUI.Controls/Utils/Motion/MoveMotionFactory.cs
@@ -11,7 +11,7 @@ public static partial class MotionFactory
public static MotionConfig BuildMoveDownInMotion(double offset, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
- easing ??= new QuinticEaseOut();
+ easing ??= new QuinticEaseOut();
var animations = new List();
RelativePoint transformOrigin = default;
var animation = new Animation()
@@ -39,9 +39,42 @@ public static partial class MotionFactory
Value = offset
};
startFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.6)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 0.1
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateYSetter = new Setter()
+ {
+ Property = TranslateTransform.YProperty,
+ Value = offset / 4
+ };
+ middleFrame.Setters.Add(translateYSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -59,9 +92,15 @@ public static partial class MotionFactory
Value = 0.0
};
endFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
-
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
animations.Add(animation);
@@ -71,7 +110,7 @@ public static partial class MotionFactory
public static MotionConfig BuildMoveDownOutMotion(double offset, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
- easing ??= new QuinticEaseIn();
+ easing ??= new QuinticEaseIn();
var animations = new List();
RelativePoint transformOrigin = default;
var animation = new Animation()
@@ -99,9 +138,42 @@ public static partial class MotionFactory
Value = 0.0
};
startFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.35)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 0.0
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateYSetter = new Setter()
+ {
+ Property = TranslateTransform.YProperty,
+ Value = offset / 2
+ };
+ middleFrame.Setters.Add(translateYSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -119,6 +191,13 @@ public static partial class MotionFactory
Value = offset
};
endFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0, 0, RelativeUnit.Relative);
@@ -130,7 +209,7 @@ public static partial class MotionFactory
public static MotionConfig BuildMoveUpInMotion(double offset, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
- easing ??= new QuinticEaseOut();
+ easing ??= new QuinticEaseOut();
var animations = new List();
RelativePoint transformOrigin = default;
var animation = new Animation()
@@ -158,9 +237,42 @@ public static partial class MotionFactory
Value = -offset
};
startFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.6)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 0.1
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateYSetter = new Setter()
+ {
+ Property = TranslateTransform.YProperty,
+ Value = -offset / 4
+ };
+ middleFrame.Setters.Add(translateYSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -178,6 +290,13 @@ public static partial class MotionFactory
Value = 0.0
};
endFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
@@ -189,7 +308,7 @@ public static partial class MotionFactory
public static MotionConfig BuildMoveUpOutMotion(double offset, TimeSpan duration, Easing? easing = null,
FillMode fillMode = FillMode.None)
{
- easing ??= new QuinticEaseIn();
+ easing ??= new QuinticEaseIn();
var animations = new List();
RelativePoint transformOrigin = default;
var animation = new Animation()
@@ -214,12 +333,45 @@ public static partial class MotionFactory
var translateYSetter = new Setter()
{
Property = TranslateTransform.YProperty,
- Value = -offset
+ Value = 0.0
};
startFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.35)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 0.0
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateYSetter = new Setter()
+ {
+ Property = TranslateTransform.YProperty,
+ Value = -offset / 2
+ };
+ middleFrame.Setters.Add(translateYSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -234,9 +386,16 @@ public static partial class MotionFactory
var translateYSetter = new Setter()
{
Property = TranslateTransform.YProperty,
- Value = 0.0
+ Value = -offset
};
endFrame.Setters.Add(translateYSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0, 0, RelativeUnit.Relative);
@@ -276,9 +435,42 @@ public static partial class MotionFactory
Value = -offset
};
startFrame.Setters.Add(translateXSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.7)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 0.0
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateXSetter = new Setter()
+ {
+ Property = TranslateTransform.XProperty,
+ Value = -offset
+ };
+ middleFrame.Setters.Add(translateXSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -296,6 +488,13 @@ public static partial class MotionFactory
Value = 0.0
};
endFrame.Setters.Add(translateXSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0.0, 0.0, RelativeUnit.Relative);
@@ -335,9 +534,42 @@ public static partial class MotionFactory
Value = 0.0
};
startFrame.Setters.Add(translateXSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
+ var middleFrame = new KeyFrame()
+ {
+ Cue = new Cue(0.75)
+ };
+ {
+ var opacitySetter = new Setter()
+ {
+ Property = Visual.OpacityProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(opacitySetter);
+ var translateXSetter = new Setter()
+ {
+ Property = TranslateTransform.XProperty,
+ Value = -offset
+ };
+ middleFrame.Setters.Add(translateXSetter);
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 1.0
+ };
+ middleFrame.Setters.Add(scaleYSetter);
+ }
+ animation.Children.Add(middleFrame);
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -355,6 +587,13 @@ public static partial class MotionFactory
Value = -offset
};
endFrame.Setters.Add(translateXSetter);
+
+ var scaleYSetter = new Setter()
+ {
+ Property = ScaleTransform.ScaleYProperty,
+ Value = 0.0
+ };
+ endFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(endFrame);
transformOrigin = new RelativePoint(0, 0, RelativeUnit.Relative);
@@ -394,7 +633,7 @@ public static partial class MotionFactory
Value = offset
};
startFrame.Setters.Add(translateXSetter);
-
+
var scaleYSetter = new Setter()
{
Property = ScaleTransform.ScaleYProperty,
@@ -403,8 +642,7 @@ public static partial class MotionFactory
startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
-
-
+
var middleFrame = new KeyFrame()
{
Cue = new Cue(0.7)
@@ -448,7 +686,7 @@ public static partial class MotionFactory
Value = 0.0
};
endFrame.Setters.Add(translateXSetter);
-
+
var scaleYSetter = new Setter()
{
Property = ScaleTransform.ScaleYProperty,
@@ -494,7 +732,7 @@ public static partial class MotionFactory
Value = 0.0
};
startFrame.Setters.Add(translateXSetter);
-
+
var scaleYSetter = new Setter()
{
Property = ScaleTransform.ScaleYProperty,
@@ -503,7 +741,7 @@ public static partial class MotionFactory
startFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(startFrame);
-
+
var middleFrame = new KeyFrame()
{
Cue = new Cue(0.75)
@@ -512,7 +750,7 @@ public static partial class MotionFactory
var opacitySetter = new Setter()
{
Property = Visual.OpacityProperty,
- Value = 0.0
+ Value = 1.0
};
middleFrame.Setters.Add(opacitySetter);
var translateXSetter = new Setter()
@@ -529,7 +767,7 @@ public static partial class MotionFactory
middleFrame.Setters.Add(scaleYSetter);
}
animation.Children.Add(middleFrame);
-
+
var endFrame = new KeyFrame()
{
Cue = new Cue(1.0)
@@ -547,7 +785,7 @@ public static partial class MotionFactory
Value = offset
};
endFrame.Setters.Add(translateXSetter);
-
+
var scaleYSetter = new Setter()
{
Property = ScaleTransform.ScaleYProperty,