From 9cbefe630740fa0c1ba119d87dea867aac84efc8 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Mon, 8 Jul 2024 15:39:48 +0800 Subject: [PATCH] feat: OverlayDialogHost remake: support host with same id in different toplevel. support modal status propagation. --- demo/Ursa.Demo/Views/MainView.axaml | 6 + .../Controls/UrsaWindow.axaml | 1 + .../Dialog/Options/OverlayDialogOptions.cs | 4 + src/Ursa/Controls/Dialog/OverlayDialog.cs | 24 ++-- src/Ursa/Controls/Drawer/Drawer.cs | 123 +++++++++--------- .../Controls/Drawer/Options/DrawerOptions.cs | 4 + src/Ursa/Controls/MessageBox/MessageBox.cs | 45 +++---- .../OverlayShared/OverlayDialogHost.Dialog.cs | 4 +- .../OverlayShared/OverlayDialogHost.Drawer.cs | 4 +- .../OverlayShared/OverlayDialogHost.Shared.cs | 43 +++++- .../OverlayShared/OverlayDialogManager.cs | 40 ++---- 11 files changed, 165 insertions(+), 133 deletions(-) diff --git a/demo/Ursa.Demo/Views/MainView.axaml b/demo/Ursa.Demo/Views/MainView.axaml index 57f79a7..65a654a 100644 --- a/demo/Ursa.Demo/Views/MainView.axaml +++ b/demo/Ursa.Demo/Views/MainView.axaml @@ -20,7 +20,13 @@ + + + + diff --git a/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs b/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs index 7ac073e..b48acea 100644 --- a/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs +++ b/src/Ursa/Controls/Dialog/Options/OverlayDialogOptions.cs @@ -46,4 +46,8 @@ public class OverlayDialogOptions public bool ShowCloseButton { get; set; } = true; public bool CanLightDismiss { get; set; } public bool CanDragMove { get; set; } = true; + /// + /// The hash code of the top level dialog host. This is used to identify the dialog host if there are multiple dialog hosts with the same id. If this is not provided, the dialog will be added to the first dialog host with the same id. + /// + public int? TopLevelHashCode { get; set; } } \ No newline at end of file diff --git a/src/Ursa/Controls/Dialog/OverlayDialog.cs b/src/Ursa/Controls/Dialog/OverlayDialog.cs index 7d57525..38cd91a 100644 --- a/src/Ursa/Controls/Dialog/OverlayDialog.cs +++ b/src/Ursa/Controls/Dialog/OverlayDialog.cs @@ -11,7 +11,7 @@ public static class OverlayDialog OverlayDialogOptions? options = null) where TView : Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var t = new DefaultDialogControl() { @@ -25,7 +25,7 @@ public static class OverlayDialog public static void Show(Control control, object? vm, string? hostId = null, OverlayDialogOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var t = new DefaultDialogControl() { @@ -39,7 +39,7 @@ public static class OverlayDialog public static void Show(object? vm, string? hostId = null, OverlayDialogOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var view = host.GetDataTemplate(vm)?.Build(vm); if (view is null) view = new ContentControl(); @@ -57,7 +57,7 @@ public static class OverlayDialog OverlayDialogOptions? options = null) where TView: Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var t = new CustomDialogControl() { @@ -71,7 +71,7 @@ public static class OverlayDialog public static void ShowCustom(Control control, object? vm, string? hostId = null, OverlayDialogOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var t = new CustomDialogControl() { @@ -85,7 +85,7 @@ public static class OverlayDialog public static void ShowCustom(object? vm, string? hostId = null, OverlayDialogOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var view = host.GetDataTemplate(vm)?.Build(vm); if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; @@ -103,7 +103,7 @@ public static class OverlayDialog OverlayDialogOptions? options = null, CancellationToken? token = default) where TView: Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(DialogResult.None); var t = new DefaultDialogControl() { @@ -118,7 +118,7 @@ public static class OverlayDialog public static Task ShowModal(Control control, object? vm, string? hostId = null, OverlayDialogOptions? options = null, CancellationToken? token = default) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(DialogResult.None); var t = new DefaultDialogControl() { @@ -134,7 +134,7 @@ public static class OverlayDialog OverlayDialogOptions? options = null, CancellationToken? token = default) where TView: Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default(TResult)); var t = new CustomDialogControl() { @@ -149,7 +149,7 @@ public static class OverlayDialog public static Task ShowCustomModal(Control control, object? vm, string? hostId = null, OverlayDialogOptions? options = null, CancellationToken? token = default) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default(TResult)); var t = new CustomDialogControl() { @@ -164,7 +164,7 @@ public static class OverlayDialog public static Task ShowCustomModal(object? vm, string? hostId = null, OverlayDialogOptions? options = null, CancellationToken? token = default) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default(TResult)); var view = host.GetDataTemplate(vm)?.Build(vm); if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; @@ -227,7 +227,7 @@ public static class OverlayDialog internal static T? Recall(string? hostId) where T: Control { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, null); if (host is null) return null; var item = host.Recall(); return item; diff --git a/src/Ursa/Controls/Drawer/Drawer.cs b/src/Ursa/Controls/Drawer/Drawer.cs index 6013040..c36a037 100644 --- a/src/Ursa/Controls/Drawer/Drawer.cs +++ b/src/Ursa/Controls/Drawer/Drawer.cs @@ -8,14 +8,14 @@ namespace Ursa.Controls; public static class Drawer { public static void Show(TViewModel vm, string? hostId = null, DrawerOptions? options = null) - where TView: Control, new() + where TView : Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = new TView(), - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddDrawer(drawer); @@ -24,12 +24,12 @@ public static class Drawer public static void Show(Control control, object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = control, - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddDrawer(drawer); @@ -37,29 +37,30 @@ public static class Drawer public static void Show(object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var view = host.GetDataTemplate(vm)?.Build(vm); - if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; + if (view is null) view = new ContentControl { Padding = new Thickness(24) }; view.DataContext = vm; - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = view, - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddDrawer(drawer); } - - public static Task ShowModal(TViewModel vm, string? hostId = null, DrawerOptions? options = null) - where TView: Control, new() + + public static Task ShowModal(TViewModel vm, string? hostId = null, + DrawerOptions? options = null) + where TView : Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(DialogResult.None); - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = new TView(), - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddModalDrawer(drawer); @@ -69,12 +70,12 @@ public static class Drawer public static Task ShowModal(Control control, object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(DialogResult.None); - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = control, - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddModalDrawer(drawer); @@ -83,110 +84,114 @@ public static class Drawer public static Task ShowModal(object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(DialogResult.None); var view = host.GetDataTemplate(vm)?.Build(vm); - if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; + if (view is null) view = new ContentControl { Padding = new Thickness(24) }; view.DataContext = vm; - var drawer = new DefaultDrawerControl() + var drawer = new DefaultDrawerControl { Content = view, - DataContext = vm, + DataContext = vm }; ConfigureDefaultDrawer(drawer, options); host.AddModalDrawer(drawer); return drawer.ShowAsync(); } - - public static void ShowCustom(TViewModel vm, string? hostId = null, DrawerOptions? options = null) - where TView: Control, new() + + public static void ShowCustom(TViewModel vm, string? hostId = null, + DrawerOptions? options = null) + where TView : Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = new TView(), - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddDrawer(dialog); } - + public static void ShowCustom(Control control, object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = control, - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddDrawer(dialog); } - + public static void ShowCustom(object? vm, string? hostId = null, DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return; var view = host.GetDataTemplate(vm)?.Build(vm); - if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; + if (view is null) view = new ContentControl { Padding = new Thickness(24) }; view.DataContext = vm; - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = view, - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddDrawer(dialog); } - - public static Task ShowCustomModal(TViewModel vm, string? hostId = null, DrawerOptions? options = null) - where TView: Control, new() + + public static Task ShowCustomModal(TViewModel vm, string? hostId = null, + DrawerOptions? options = null) + where TView : Control, new() { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default); - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = new TView(), - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddModalDrawer(dialog); return dialog.ShowAsync(); } - - public static Task ShowCustomModal(Control control, object? vm, string? hostId = null, DrawerOptions? options = null) + + public static Task ShowCustomModal(Control control, object? vm, string? hostId = null, + DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default); - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = control, - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddModalDrawer(dialog); return dialog.ShowAsync(); } - - public static Task ShowCustomModal(object? vm, string? hostId = null, DrawerOptions? options = null) + + public static Task ShowCustomModal(object? vm, string? hostId = null, + DrawerOptions? options = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, options?.TopLevelHashCode); if (host is null) return Task.FromResult(default); var view = host.GetDataTemplate(vm)?.Build(vm); - if (view is null) view = new ContentControl() { Padding = new Thickness(24) }; + if (view is null) view = new ContentControl { Padding = new Thickness(24) }; view.DataContext = vm; - var dialog = new CustomDrawerControl() + var dialog = new CustomDrawerControl { Content = view, - DataContext = vm, + DataContext = vm }; ConfigureCustomDrawer(dialog, options); host.AddModalDrawer(dialog); return dialog.ShowAsync(); } - + private static void ConfigureCustomDrawer(CustomDrawerControl drawer, DrawerOptions? options) { options ??= DrawerOptions.Default; @@ -198,13 +203,14 @@ public static class Drawer drawer.MinWidth = options.MinWidth ?? 0.0; drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity; } + if (options.Position is Position.Top or Position.Bottom) { drawer.MinHeight = options.MinHeight ?? 0.0; drawer.MaxHeight = options.MaxHeight ?? double.PositiveInfinity; } } - + private static void ConfigureDefaultDrawer(DefaultDrawerControl drawer, DrawerOptions? options) { options ??= DrawerOptions.Default; @@ -218,6 +224,7 @@ public static class Drawer drawer.MinWidth = options.MinWidth ?? 0.0; drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity; } + if (options.Position is Position.Top or Position.Bottom) { drawer.MinHeight = options.MinHeight ?? 0.0; diff --git a/src/Ursa/Controls/Drawer/Options/DrawerOptions.cs b/src/Ursa/Controls/Drawer/Options/DrawerOptions.cs index a0cf4dc..6d1afe2 100644 --- a/src/Ursa/Controls/Drawer/Options/DrawerOptions.cs +++ b/src/Ursa/Controls/Drawer/Options/DrawerOptions.cs @@ -15,4 +15,8 @@ public class DrawerOptions public DialogButton Buttons { get; set; } = DialogButton.OKCancel; public string? Title { get; set; } public bool ShowCloseButton { get; set; } = true; + /// + /// The hash code of the top level dialog host. This is used to identify the dialog host if there are multiple dialog hosts with the same id. If this is not provided, the dialog will be added to the first dialog host with the same id. + /// + public int? TopLevelHashCode { get; set; } } \ No newline at end of file diff --git a/src/Ursa/Controls/MessageBox/MessageBox.cs b/src/Ursa/Controls/MessageBox/MessageBox.cs index 59c1657..6008947 100644 --- a/src/Ursa/Controls/MessageBox/MessageBox.cs +++ b/src/Ursa/Controls/MessageBox/MessageBox.cs @@ -1,15 +1,13 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Controls.Notifications; -using Avalonia.Styling; namespace Ursa.Controls; public static class MessageBox { public static async Task ShowAsync( - string message, + string message, string? title = null, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxButton button = MessageBoxButton.OKCancel) @@ -18,33 +16,25 @@ public static class MessageBox { Content = message, Title = title, - MessageIcon = icon, + MessageIcon = icon }; var lifetime = Application.Current?.ApplicationLifetime; - if (lifetime is IClassicDesktopStyleApplicationLifetime classLifetime) - { - var main = classLifetime.MainWindow; - if (main is null) - { - messageWindow.Show(); - return MessageBoxResult.None; - } - else - { - var result = await messageWindow.ShowDialog(main); - return result; - } - } - else + if (lifetime is not IClassicDesktopStyleApplicationLifetime classLifetime) return MessageBoxResult.None; + var main = classLifetime.MainWindow; + if (main is null) { + messageWindow.Show(); return MessageBoxResult.None; } + + var result = await messageWindow.ShowDialog(main); + return result; } - + public static async Task ShowAsync( Window owner, - string message, - string title, + string message, + string title, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxButton button = MessageBoxButton.OKCancel) { @@ -52,7 +42,7 @@ public static class MessageBox { Content = message, Title = title, - MessageIcon = icon, + MessageIcon = icon }; var result = await messageWindow.ShowDialog(owner); return result; @@ -63,16 +53,17 @@ public static class MessageBox string? title = null, string? hostId = null, MessageBoxIcon icon = MessageBoxIcon.None, - MessageBoxButton button = MessageBoxButton.OKCancel) + MessageBoxButton button = MessageBoxButton.OKCancel, + int? toplevelHashCode = null) { - var host = OverlayDialogManager.GetHost(hostId); + var host = OverlayDialogManager.GetHost(hostId, toplevelHashCode); if (host is null) return MessageBoxResult.None; - var messageControl = new MessageBoxControl() + var messageControl = new MessageBoxControl { Content = message, Title = title, Buttons = button, - MessageIcon = icon, + MessageIcon = icon }; host.AddModalDialog(messageControl); var result = await messageControl.ShowAsync(); diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs index 499d998..f41b26c 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Dialog.cs @@ -129,7 +129,7 @@ public partial class OverlayDialogHost if (layer.Modal) { _modalCount--; - HasModal = _modalCount > 0; + IsInModalStatus = _modalCount > 0; if (!IsAnimationDisabled) { await _maskDisappearAnimation.RunAsync(layer.Mask); @@ -170,7 +170,7 @@ public partial class OverlayDialogHost _maskAppearAnimation.RunAsync(mask); } _modalCount++; - HasModal = _modalCount > 0; + IsInModalStatus = _modalCount > 0; control.IsClosed = false; } diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs index d7a1bfa..6a893ba 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs @@ -56,7 +56,7 @@ public partial class OverlayDialogHost control.Arrange(new Rect(control.DesiredSize)); SetDrawerPosition(control); _modalCount++; - HasModal = _modalCount > 0; + IsInModalStatus = _modalCount > 0; control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDrawerControlClosing); var animation = CreateAnimation(control.Bounds.Size, control.Position); if (IsAnimationDisabled) @@ -162,7 +162,7 @@ public partial class OverlayDialogHost if (layer.Mask is not null) { _modalCount--; - HasModal = _modalCount > 0; + IsInModalStatus = _modalCount > 0; layer.Mask.RemoveHandler(PointerPressedEvent, ClickMaskToCloseDialog); if (!IsAnimationDisabled) { diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs index e993696..e314622 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs @@ -6,6 +6,8 @@ using Avalonia.Input; using Avalonia.Media; using Ursa.Controls.OverlayShared; using Avalonia.Styling; +using Avalonia.VisualTree; +using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Shapes; namespace Ursa.Controls; @@ -32,18 +34,35 @@ public partial class OverlayDialogHost: Canvas } private int _modalCount = 0; - - public static readonly DirectProperty HasModalProperty = AvaloniaProperty.RegisterDirect( nameof(HasModal), o => o.HasModal); private bool _hasModal; + [Obsolete("Use IsInModalStatus")] public bool HasModal { get => _hasModal; private set => SetAndRaise(HasModalProperty, ref _hasModal, value); } + public static readonly AttachedProperty IsModalStatusScopeProperty = + AvaloniaProperty.RegisterAttached("IsModalStatusScope"); + + public static void SetIsModalStatusScope(Control obj, bool value) => obj.SetValue(IsModalStatusScopeProperty, value); + internal static bool GetIsModalStatusScope(Control obj) => obj.GetValue(IsModalStatusScopeProperty); + + public static readonly AttachedProperty IsInModalStatusProperty = + AvaloniaProperty.RegisterAttached(nameof(IsInModalStatus)); + + internal static void SetIsInModalStatus(Control obj, bool value) => obj.SetValue(IsInModalStatusProperty, value); + public static bool GetIsInModalStatus(Control obj) => obj.GetValue(IsInModalStatusProperty); + + public bool IsInModalStatus + { + get => GetValue(IsInModalStatusProperty); + set => SetValue(IsInModalStatusProperty, value); + } + public bool IsAnimationDisabled { get; set; } static OverlayDialogHost() @@ -51,6 +70,11 @@ public partial class OverlayDialogHost: Canvas ClipToBoundsProperty.OverrideDefaultValue(true); _maskAppearAnimation = CreateOpacityAnimation(true); _maskDisappearAnimation = CreateOpacityAnimation(false); + // This is only a temporary solution, will be removed in release candidate mode. + IsInModalStatusProperty.Changed.AddClassHandler((host, args) => + { + host.HasModal = args.NewValue.Value; + }); } private static Animation CreateOpacityAnimation(bool appear) @@ -116,11 +140,18 @@ public partial class OverlayDialogHost: Canvas } } } - + private IDisposable? _modalStatusSubscription; protected sealed override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - OverlayDialogManager.RegisterHost(this, HostId); + var hash = TopLevel.GetTopLevel(this)?.GetHashCode(); + var modalHost = this.GetVisualAncestors().OfType().FirstOrDefault(GetIsModalStatusScope); + if (modalHost is not null) + { + _modalStatusSubscription = this.GetObservable(IsInModalStatusProperty) + .Subscribe(a => OverlayDialogHost.SetIsInModalStatus(modalHost, a)); + } + OverlayDialogManager.RegisterHost(this, HostId, hash); } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) @@ -129,7 +160,9 @@ public partial class OverlayDialogHost: Canvas { _layers[0].Element.Close(); } - OverlayDialogManager.UnregisterHost(HostId); + _modalStatusSubscription?.Dispose(); + var hash = TopLevel.GetTopLevel(this)?.GetHashCode(); + OverlayDialogManager.UnregisterHost(HostId, hash); base.OnDetachedFromVisualTree(e); } diff --git a/src/Ursa/Controls/OverlayShared/OverlayDialogManager.cs b/src/Ursa/Controls/OverlayShared/OverlayDialogManager.cs index 82e88af..685d690 100644 --- a/src/Ursa/Controls/OverlayShared/OverlayDialogManager.cs +++ b/src/Ursa/Controls/OverlayShared/OverlayDialogManager.cs @@ -2,41 +2,27 @@ using System.Collections.Concurrent; namespace Ursa.Controls; +internal record struct HostKey(string? Id, int? Hash); + internal static class OverlayDialogManager { - private static OverlayDialogHost? _defaultHost; - private static readonly ConcurrentDictionary Hosts = new(); + private static readonly ConcurrentDictionary Hosts = new(); - public static void RegisterHost(OverlayDialogHost host, string? id) + public static void RegisterHost(OverlayDialogHost host, string? id, int? hash) { - if (id == null) - { - if (_defaultHost != null) - { - throw new InvalidOperationException("Cannot register multiple OverlayDialogHost with empty HostId"); - } - _defaultHost = host; - return; - } - Hosts.TryAdd(id, host); + Hosts.TryAdd(new HostKey(id, hash), host); } - public static void UnregisterHost(string? id) + public static void UnregisterHost(string? id, int? hash) { - if (id is null) - { - _defaultHost = null; - return; - } - Hosts.TryRemove(id, out _); + Hosts.TryRemove(new HostKey(id, hash), out _); } - public static OverlayDialogHost? GetHost(string? id) + public static OverlayDialogHost? GetHost(string? id, int? hash) { - if (id is null) - { - return _defaultHost; - } - return Hosts.TryGetValue(id, out var host) ? host : null; + HostKey? key = hash is null ? Hosts.Keys.Where(k => k.Id == id).ToArray().FirstOrDefault() : Hosts.Keys.FirstOrDefault(k => k.Id == id && k.Hash == hash); + if (key is null) return null; + return Hosts.TryGetValue(key.Value, out var host) ? host : null; } -} \ No newline at end of file +} +