完成 Notification placement

This commit is contained in:
polarboy 2024-08-30 14:22:03 +08:00
parent 3b8c81678d
commit 3c5d7c3c6e
9 changed files with 774 additions and 49 deletions

View File

@ -16,5 +16,76 @@
Show Notification
</atom:Button>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Duration after which the notification box is closed"
Description="Duration can be used to specify how long the notification stays open. After the duration time elapses, the notification closes automatically. If not specified, default value is 4.5 seconds. If you set the value to TimeSpan.Zero, the notification box will never close automatically.">
<atom:Button ButtonType="Primary"
Click="ShowNeverCloseNotification">
Open the notification box
</atom:Button>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Notification with icon"
Description="A notification box with a icon at the left side.">
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Button ButtonType="Default"
Click="ShowSuccessNotification">
Success
</atom:Button>
<atom:Button ButtonType="Default"
Click="ShowInfoNotification">
Info
</atom:Button>
<atom:Button ButtonType="Default"
Click="ShowWarningNotification">
Warning
</atom:Button>
<atom:Button ButtonType="Default"
Click="ShowErrorNotification">
Error
</atom:Button>
</StackPanel>
</showcase:ShowCaseItem>
<showcase:ShowCaseItem
Title="Placement"
Description="A notification box can appear from the top bottom topLeft topRight bottomLeft or bottomRight of the viewport via placement.">
<StackPanel Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Button ButtonType="Primary"
Click="ShowTopNotification">
Top
</atom:Button>
<atom:Button ButtonType="Primary"
Click="ShowBottomNotification">
Bottom
</atom:Button>
</StackPanel>
<atom:Separator/>
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Button ButtonType="Primary"
Click="ShowTopLeftNotification">
TopLeft
</atom:Button>
<atom:Button ButtonType="Primary"
Click="ShowTopRightNotification">
TopRight
</atom:Button>
</StackPanel>
<atom:Separator/>
<StackPanel Orientation="Horizontal" Spacing="10">
<atom:Button ButtonType="Primary"
Click="ShowBottomLeftNotification">
BottomLeft
</atom:Button>
<atom:Button ButtonType="Primary"
Click="ShowBottomRightNotification">
BottomRight
</atom:Button>
</StackPanel>
</StackPanel>
</showcase:ShowCaseItem>
</showcase:ShowCasePanel>
</UserControl>

View File

@ -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!"
});
}
}

View File

@ -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");

View File

@ -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<NotificationCard, bool> EffectiveShowProgressProperty =
AvaloniaProperty.RegisterDirect<NotificationCard, bool>(nameof(EffectiveShowProgress),
o => o.EffectiveShowProgress);
o => o.EffectiveShowProgress,
(o, v) => o.EffectiveShowProgress = v);
internal static readonly DirectProperty<NotificationCard, NotificationPosition> PositionProperty =
AvaloniaProperty.RegisterDirect<NotificationCard, NotificationPosition>(
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
/// <summary>
@ -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<NotificationPosition>());
}
}
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);
}
}

View File

@ -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);
// 防止关闭的时候抖动,如果直接设置到 NotificationCardlayoutTransformControl没有办法平滑处理
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);
}
}

View File

@ -50,9 +50,14 @@ internal class NotificationToken : AbstractControlDesignToken
public Thickness NotificationMarginBottom { get; set; }
/// <summary>
/// 提醒框边缘外边距
/// 提醒框边缘外边距
/// </summary>
public Thickness NotificationMarginEdge { get; set; }
public Thickness NotificationTopMargin { get; set; }
/// <summary>
/// 提醒框下边缘外边距
/// </summary>
public Thickness NotificationBottomMargin { get; set; }
/// <summary>
/// 提醒框进度条背景色
@ -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()
{

View File

@ -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);

View File

@ -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);

View File

@ -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<IAnimation>();
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<IAnimation>();
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<IAnimation>();
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<IAnimation>();
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,