Browse Source

fix: light dismiss behavior fails when opened from the ContextMenu. (#22037)

* fix: light dismiss behavior fails when opened from the ContextMenu.

* 1. LightDismissOverlayLayer is invisible by default.
2. Eliminate misinformation that Registration may bring.
3. Remove LightDismissOverlayLayer Manually set IsVisible to false at creation time.

---------

Co-authored-by: Steven Kirk <grokys@users.noreply.github.com>
pull/22083/head
Lin 1 month ago
committed by GitHub
parent
commit
6b185ed56a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 40
      src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs
  2. 9
      src/Avalonia.Controls/Primitives/Popup.cs
  3. 5
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  4. 35
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

40
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
/// </summary>
internal class LightDismissOverlayLayer : Border, ICustomHitTest
{
public IInputElement? InputPassThroughElement { get; set; }
private readonly List<Registration> _registrations = [];
public IInputElement? InputPassThroughElement => _registrations.LastOrDefault()?.InputPassThroughElement;
static LightDismissOverlayLayer()
{
BackgroundProperty.OverrideDefaultValue<LightDismissOverlayLayer>(Brushes.Transparent);
IsVisibleProperty.OverrideDefaultValue<LightDismissOverlayLayer>(false);
}
/// <summary>
@ -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;
}
/// <inheritdoc />
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);
}
}
}
}

9
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<LightDismissOverlayLayer, EventHandler<PointerPressedEventArgs>>(
dismissLayer,

5
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -109,10 +109,7 @@ namespace Avalonia.Controls.Primitives
var rv = FindLayer<LightDismissOverlayLayer>();
if (rv == null)
{
rv = new LightDismissOverlayLayer
{
IsVisible = false
};
rv = new LightDismissOverlayLayer();
AddLayer(rv, LightDismissOverlayZIndex);
}

35
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()

Loading…
Cancel
Save