diff --git a/src/Artemis.UI.Shared/Controls/NotificationHost.cs b/src/Artemis.UI.Shared/Controls/NotificationHost.cs
new file mode 100644
index 000000000..4dce59804
--- /dev/null
+++ b/src/Artemis.UI.Shared/Controls/NotificationHost.cs
@@ -0,0 +1,54 @@
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Layout;
+
+namespace Artemis.UI.Shared;
+
+internal class NotificationHost : ContentControl
+{
+ private IDisposable? _rootBoundsWatcher;
+
+ public NotificationHost()
+ {
+ Background = null;
+ HorizontalAlignment = HorizontalAlignment.Center;
+ VerticalAlignment = VerticalAlignment.Center;
+ }
+
+ protected override Type StyleKeyOverride => typeof(OverlayPopupHost);
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ _ = base.MeasureOverride(availableSize);
+
+ if (VisualRoot is TopLevel tl)
+ return tl.ClientSize;
+ if (VisualRoot is Control c)
+ return c.Bounds.Size;
+
+ return default;
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+ if (e.Root is Control wb)
+ // OverlayLayer is a Canvas, so we won't get a signal to resize if the window
+ // bounds change. Subscribe to force update
+ _rootBoundsWatcher = wb.GetObservable(BoundsProperty).Subscribe(_ => OnRootBoundsChanged());
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _rootBoundsWatcher?.Dispose();
+ _rootBoundsWatcher = null;
+ }
+
+ private void OnRootBoundsChanged()
+ {
+ InvalidateMeasure();
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI.Shared/Services/Builders/NotificationBuilder.cs b/src/Artemis.UI.Shared/Services/Builders/NotificationBuilder.cs
index 5dfe0ecb9..805c5cbd0 100644
--- a/src/Artemis.UI.Shared/Services/Builders/NotificationBuilder.cs
+++ b/src/Artemis.UI.Shared/Services/Builders/NotificationBuilder.cs
@@ -2,11 +2,11 @@
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using ReactiveUI;
-using Button = Avalonia.Controls.Button;
namespace Artemis.UI.Shared.Services.Builders;
@@ -117,34 +117,34 @@ public class NotificationBuilder
///
public Action Show()
{
- Panel? panel = _parent.Find("NotificationContainer");
- if (panel == null)
- throw new ArtemisSharedUIException("Can't display a notification on a window without a NotificationContainer.");
-
Dispatcher.UIThread.Post(() =>
{
- panel.Children.Add(_infoBar);
+ OverlayLayer? overlayLayer = OverlayLayer.GetOverlayLayer(_parent);
+ if (overlayLayer == null)
+ throw new ArtemisSharedUIException("Can't display a notification on a window an overlay layer.");
+
+ NotificationHost container = new() {Content = _infoBar};
+ overlayLayer.Children.Add(container);
_infoBar.Closed += InfoBarOnClosed;
_infoBar.IsOpen = true;
- });
- Task.Run(async () =>
- {
- await Task.Delay(_timeout);
- Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
+ Dispatcher.UIThread.InvokeAsync(async () =>
+ {
+ await Task.Delay(_timeout);
+ _infoBar.IsOpen = false;
+ });
+
+ return;
+
+ void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
+ {
+ overlayLayer.Children.Remove(container);
+ _infoBar.Closed -= InfoBarOnClosed;
+ }
});
return () => Dispatcher.UIThread.Post(() => _infoBar.IsOpen = false);
}
-
- private void InfoBarOnClosed(InfoBar sender, InfoBarClosedEventArgs args)
- {
- _infoBar.Closed -= InfoBarOnClosed;
- if (_parent.Content is not Panel panel)
- return;
-
- panel.Children.Remove(_infoBar);
- }
}
///
@@ -180,7 +180,7 @@ public class NotificationButtonBuilder
_action = action;
return this;
}
-
+
///
/// Changes action that is called when the button is clicked.
///
@@ -222,9 +222,13 @@ public class NotificationButtonBuilder
button.Classes.Add("AppBarButton");
if (_action != null)
+ {
button.Command = ReactiveCommand.Create(() => _action());
+ }
else if (_asyncAction != null)
+ {
button.Command = ReactiveCommand.CreateFromTask(() => _asyncAction());
+ }
else if (_command != null)
{
button.Command = _command;
diff --git a/src/Artemis.UI.Shared/Styles/InfoBar.axaml b/src/Artemis.UI.Shared/Styles/InfoBar.axaml
index 3f9b4a9bc..7b6f7f62c 100644
--- a/src/Artemis.UI.Shared/Styles/InfoBar.axaml
+++ b/src/Artemis.UI.Shared/Styles/InfoBar.axaml
@@ -17,6 +17,8 @@
+
+
diff --git a/src/Artemis.UI.Shared/Styles/Notifications.axaml b/src/Artemis.UI.Shared/Styles/Notifications.axaml
index 0e9553ca2..94bc9039c 100644
--- a/src/Artemis.UI.Shared/Styles/Notifications.axaml
+++ b/src/Artemis.UI.Shared/Styles/Notifications.axaml
@@ -1,17 +1,14 @@
+ xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
+ xmlns:shared="clr-namespace:Artemis.UI.Shared">
-
-
-