using System; using System.Collections; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls.Primitives; using Avalonia.Rendering; using Avalonia.Data; using Avalonia.VisualTree; using Avalonia.Controls.Metadata; namespace Avalonia.Controls.Notifications { /// /// An that displays notifications in a . /// [PseudoClasses(":topleft", ":topright", ":bottomleft", ":bottomright")] public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest { private IList _items; /// /// 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 { return GetValue(PositionProperty); } set { SetValue(PositionProperty, value); } } /// /// Defines the property. /// public static readonly StyledProperty MaxItemsProperty = AvaloniaProperty.Register(nameof(MaxItems), 5); /// /// Defines the maximum number of notifications visible at once. /// public int MaxItems { get { return GetValue(MaxItemsProperty); } set { SetValue(MaxItemsProperty, value); } } /// /// Initializes a new instance of the class. /// /// The window that will host the control. public WindowNotificationManager(Window host) { if (VisualChildren.Count != 0) { Install(host); } else { Observable.FromEventPattern(host, nameof(host.TemplateApplied)).Take(1) .Subscribe(_ => { Install(host); }); } UpdatePseudoClasses(Position); } static WindowNotificationManager() { HorizontalAlignmentProperty.OverrideDefaultValue(Layout.HorizontalAlignment.Stretch); VerticalAlignmentProperty.OverrideDefaultValue(Layout.VerticalAlignment.Stretch); } /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { var itemsControl = e.NameScope.Find("PART_Items"); _items = itemsControl?.Children; } /// public void Show(INotification content) { Show(content as object); } /// public async void Show(object content) { var notification = content as INotification; var notificationControl = new NotificationCard { Content = content }; if (notification != null) { notificationControl.NotificationClosed += (sender, args) => { notification.OnClose?.Invoke(); _items.Remove(sender); }; } notificationControl.PointerPressed += (sender, args) => { if (notification != null && notification.OnClick != null) { notification.OnClick.Invoke(); } (sender as NotificationCard)?.Close(); }; _items.Add(notificationControl); if (_items.OfType().Count(i => !i.IsClosing) > MaxItems) { _items.OfType().First(i => !i.IsClosing).Close(); } if (notification != null && notification.Expiration == TimeSpan.Zero) { return; } await Task.Delay(notification?.Expiration ?? TimeSpan.FromSeconds(5)); notificationControl.Close(); } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == PositionProperty) { UpdatePseudoClasses(change.NewValue.GetValueOrDefault()); } } /// /// Installs the within the /// of the host . /// /// The that will be the host. private void Install(Window host) { var adornerLayer = host.FindDescendantOfType()?.AdornerLayer; adornerLayer?.Children.Add(this); } private void UpdatePseudoClasses(NotificationPosition position) { PseudoClasses.Set(":topleft", position == NotificationPosition.TopLeft); PseudoClasses.Set(":topright", position == NotificationPosition.TopRight); PseudoClasses.Set(":bottomleft", position == NotificationPosition.BottomLeft); PseudoClasses.Set(":bottomright", position == NotificationPosition.BottomRight); } public bool HitTest(Point point) => VisualChildren.HitTestCustom(point); } }