diff --git a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs index 4662d2532e..da51cdf1e7 100644 --- a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs +++ b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Threading; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Media; @@ -14,11 +16,14 @@ namespace Avalonia.Controls.Primitives /// internal class LightDismissOverlayLayer : Border, ICustomHitTest { - public IInputElement? InputPassThroughElement { get; set; } + private readonly List _registrations = []; + + public IInputElement? InputPassThroughElement => _registrations.LastOrDefault()?.InputPassThroughElement; static LightDismissOverlayLayer() { BackgroundProperty.OverrideDefaultValue(Brushes.Transparent); + IsVisibleProperty.OverrideDefaultValue(false); } /// @@ -46,6 +51,14 @@ namespace Avalonia.Controls.Primitives return manager?.LightDismissOverlayLayer; } + public IDisposable Register(IInputElement? inputPassThroughElement) + { + var registration = new Registration(this, inputPassThroughElement); + _registrations.Add(registration); + UpdateState(); + return registration; + } + /// public bool HitTest(Point point) { @@ -59,5 +72,30 @@ namespace Avalonia.Controls.Primitives return true; } + + private void Unregister(Registration registration) + { + _registrations.Remove(registration); + UpdateState(); + } + + private void UpdateState() + { + IsVisible = _registrations.Count > 0; + } + + private sealed class Registration(LightDismissOverlayLayer owner, IInputElement? inputPassThroughElement) : IDisposable + { + private LightDismissOverlayLayer? _owner = owner; + + public IInputElement? InputPassThroughElement { get; } = inputPassThroughElement; + + public void Dispose() + { + var owner = _owner; + _owner = null; + owner?.Unregister(this); + } + } } } diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index ba9ca5b572..46ab0b391a 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -565,14 +565,7 @@ namespace Avalonia.Controls.Primitives if (dismissLayer != null) { - dismissLayer.IsVisible = true; - dismissLayer.InputPassThroughElement = OverlayInputPassThroughElement; - - Disposable.Create(() => - { - dismissLayer.IsVisible = false; - dismissLayer.InputPassThroughElement = null; - }).DisposeWith(handlerCleanup); + dismissLayer.Register(OverlayInputPassThroughElement).DisposeWith(handlerCleanup); SubscribeToEventHandler>( dismissLayer, diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index fd5c55ef62..573926d2cc 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -109,10 +109,7 @@ namespace Avalonia.Controls.Primitives var rv = FindLayer(); if (rv == null) { - rv = new LightDismissOverlayLayer - { - IsVisible = false - }; + rv = new LightDismissOverlayLayer(); AddLayer(rv, LightDismissOverlayZIndex); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 3548cd614f..c06ed62a5c 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -1391,6 +1391,41 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(true, target.IsUsingOverlayLayer); } } + + [Fact] + public void Closing_Previous_Light_Dismiss_Popup_Should_Not_Affect_Overlay_For_Next_Popup() + { + using (CreateServices()) + { + var placementTarget = new Border(); + var window = PreparedWindow(placementTarget); + var first = new Popup + { + PlacementTarget = placementTarget, + IsLightDismissEnabled = true, + }; + var second = new Popup + { + PlacementTarget = placementTarget, + IsLightDismissEnabled = true, + }; + + first.Open(); + second.Open(); + + var overlay = LightDismissOverlayLayer.GetLightDismissOverlayLayer(window); + Assert.NotNull(overlay); + + first.Close(); + + Assert.True(overlay.IsVisible); + + overlay.RaiseEvent(CreatePointerPressedEventArgs(window, new Point(10, 15))); + + Assert.False(second.IsOpen); + Assert.False(overlay.IsVisible); + } + } [Fact] public void Opened_Popup_Should_Be_In_OpenedPopups()