using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Notifications; using Avalonia.Controls.Primitives; using Avalonia.Layout; using Avalonia.Threading; using Avalonia.VisualTree; namespace Ursa.Controls; /// /// An that displays notifications in a . /// [PseudoClasses(PC_TopLeft, PC_TopRight, PC_BottomLeft, PC_BottomRight, PC_TopCenter, PC_BottomCenter)] public class WindowNotificationManager : WindowMessageManager, INotificationManager { public const string PC_TopLeft = ":topleft"; public const string PC_TopRight = ":topright"; public const string PC_BottomLeft = ":bottomleft"; public const string PC_BottomRight = ":bottomright"; public const string PC_TopCenter = ":topcenter"; public const string PC_BottomCenter = ":bottomcenter"; /// /// Defines the property. /// public static readonly StyledProperty PositionProperty = AvaloniaProperty.Register(nameof(Position), NotificationPosition.TopRight); /// /// Defines which corner of the screen notifications can be displayed in. /// /// public NotificationPosition Position { get => GetValue(PositionProperty); set => SetValue(PositionProperty, value); } /// /// Initializes a new instance of the class. /// public WindowNotificationManager() { UpdatePseudoClasses(Position); } /// /// Initializes a new instance of the class. /// /// The TopLevel that will host the control. public WindowNotificationManager(TopLevel? host) : this() { if (host is not null) { InstallFromTopLevel(host); } } public WindowNotificationManager(VisualLayerManager? visualLayerManager) : base(visualLayerManager) { UpdatePseudoClasses(Position); } static WindowNotificationManager() { HorizontalAlignmentProperty.OverrideDefaultValue(HorizontalAlignment.Stretch); VerticalAlignmentProperty.OverrideDefaultValue(VerticalAlignment.Stretch); } /// /// Tries to get the from a . /// /// A that is either a or a . /// The existing if found, or null if not found. /// True if a is found; otherwise, false. public static bool TryGetNotificationManager(Visual? visual, out WindowNotificationManager? manager) { manager = visual?.FindDescendantOfType(); return manager is not null; } /// public void Show(INotification content) { Show(content, content.Type, content.Expiration, content.ShowIcon, content.ShowClose, content.OnClick, content.OnClose); } /// public override void Show(object content) { if (content is INotification notification) { Show(notification, notification.Type, notification.Expiration, notification.ShowIcon, notification.ShowClose, notification.OnClick, notification.OnClose); } else { Show(content, NotificationType.Information); } } /// /// Shows a Notification /// /// the content of the notification /// the type of the notification /// the expiration time of the notification after which it will automatically close. If the value is Zero then the notification will remain open until the user closes it /// whether to show the icon /// whether to show the close button /// an Action to be run when the notification is clicked /// an Action to be run when the notification is closed /// style classes to apply public async void Show( object content, NotificationType type, TimeSpan? expiration = null, bool showIcon = true, bool showClose = true, Action? onClick = null, Action? onClose = null, string[]? classes = null) { Dispatcher.UIThread.VerifyAccess(); var notificationControl = new NotificationCard { Content = content, NotificationType = type, ShowIcon = showIcon, ShowClose = showClose, [!NotificationCard.PositionProperty] = this[!PositionProperty] }; // Add style classes if any if (classes is not null) { foreach (var @class in classes) { notificationControl.Classes.Add(@class); } } notificationControl.MessageClosed += (sender, _) => { onClose?.Invoke(); _items?.Remove(sender); }; notificationControl.PointerPressed += (_, _) => { onClick?.Invoke(); }; Dispatcher.UIThread.Post(() => { _items?.Add(notificationControl); if (_items?.OfType().Count(i => !i.IsClosing) > MaxItems) { _items.OfType().First(i => !i.IsClosing).Close(); } }); if (expiration == TimeSpan.Zero) { return; } await Task.Delay(expiration ?? TimeSpan.FromSeconds(3)); notificationControl.Close(); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == PositionProperty) { UpdatePseudoClasses(change.GetNewValue()); } } private void UpdatePseudoClasses(NotificationPosition position) { PseudoClasses.Set(PC_TopLeft, position == NotificationPosition.TopLeft); PseudoClasses.Set(PC_TopRight, position == NotificationPosition.TopRight); PseudoClasses.Set(PC_BottomLeft, position == NotificationPosition.BottomLeft); PseudoClasses.Set(PC_BottomRight, position == NotificationPosition.BottomRight); PseudoClasses.Set(PC_TopCenter, position == NotificationPosition.TopCenter); PseudoClasses.Set(PC_BottomCenter, position == NotificationPosition.BottomCenter); } }