feat: OverlayDialogHost remake: support host with same id in different toplevel. support modal status propagation.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<OverlayDialogHost, bool> HasModalProperty = AvaloniaProperty.RegisterDirect<OverlayDialogHost, bool>(
|
||||
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<bool> IsModalStatusScopeProperty =
|
||||
AvaloniaProperty.RegisterAttached<OverlayDialogHost, Control, bool>("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<bool> IsInModalStatusProperty =
|
||||
AvaloniaProperty.RegisterAttached<OverlayDialogHost, Control, bool>(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<OverlayDialogHost>(true);
|
||||
_maskAppearAnimation = CreateOpacityAnimation(true);
|
||||
_maskDisappearAnimation = CreateOpacityAnimation(false);
|
||||
// This is only a temporary solution, will be removed in release candidate mode.
|
||||
IsInModalStatusProperty.Changed.AddClassHandler<OverlayDialogHost, bool>((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<Control>().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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string, OverlayDialogHost> Hosts = new();
|
||||
private static readonly ConcurrentDictionary<HostKey, OverlayDialogHost> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user