From 2bdb914dd1cd58d5aca75bb08d6e4ea96b29692b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 9 Jul 2020 12:20:46 +0200 Subject: [PATCH] Show overlay layer when popup StaysOpen = false. We need to stop events getting to parent window. Pointer capture won't work because this now needs an input event in order to take pointer capture, and filtering events on the parent window causes tooltips to continue tooltips to get stuck. Best solution would seem to be using an overlay layer. --- .../Primitives/LightDismissOverlayLayer.cs | 10 ++++++++ src/Avalonia.Controls/Primitives/Popup.cs | 21 +++++++++++++--- .../Primitives/VisualLayerManager.cs | 25 ++++++++++++++++++- 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs diff --git a/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs new file mode 100644 index 0000000000..65a21a563a --- /dev/null +++ b/src/Avalonia.Controls/Primitives/LightDismissOverlayLayer.cs @@ -0,0 +1,10 @@ +using System.Linq; +using Avalonia.Rendering; +using Avalonia.VisualTree; + +namespace Avalonia.Controls.Primitives +{ + public class LightDismissOverlayLayer : Border + { + } +} diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 1fcf8d61bc..01ae6fbf43 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -369,8 +369,6 @@ namespace Avalonia.Controls.Primitives } } - DeferCleanup(topLevel.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel)); - DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick)); var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state => @@ -384,6 +382,23 @@ namespace Avalonia.Controls.Primitives state.popupHost.Dispose(); }); + if (!StaysOpen) + { + var layerManager = placementTarget.FindAncestorOfType(); + var dismissLayer = layerManager?.LightDismissOverlayLayer; + + if (dismissLayer != null) + { + dismissLayer.IsVisible = true; + DeferCleanup(Disposable.Create(() => dismissLayer.IsVisible = false)); + DeferCleanup(SubscribeToEventHandler>( + dismissLayer, + PointerPressedDismissOverlay, + (x, handler) => x.PointerPressed += handler, + (x, handler) => x.PointerPressed -= handler)); + } + } + _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup); WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); @@ -504,7 +519,7 @@ namespace Avalonia.Controls.Primitives } } - private void PointerPressedOutside(object sender, PointerPressedEventArgs e) + private void PointerPressedDismissOverlay(object sender, PointerPressedEventArgs e) { if (!StaysOpen && e.Source is IVisual v && !IsChildOrThis(v)) { diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index 3084d7fa72..a4e230e2f4 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Avalonia.LogicalTree; +using Avalonia.Media; namespace Avalonia.Controls.Primitives { @@ -7,7 +8,8 @@ namespace Avalonia.Controls.Primitives { private const int AdornerZIndex = int.MaxValue - 100; private const int ChromeZIndex = int.MaxValue - 99; - private const int OverlayZIndex = int.MaxValue - 98; + private const int LightDismissOverlayZIndex = int.MaxValue - 98; + private const int OverlayZIndex = int.MaxValue - 97; private ILogicalRoot _logicalRoot; private readonly List _layers = new List(); @@ -50,6 +52,27 @@ namespace Avalonia.Controls.Primitives } } + public LightDismissOverlayLayer LightDismissOverlayLayer + { + get + { + if (IsPopup) + return null; + var rv = FindLayer(); + if (rv == null) + { + rv = new LightDismissOverlayLayer + { + Background = Brushes.Transparent, + IsVisible = false + }; + + AddLayer(rv, LightDismissOverlayZIndex); + } + return rv; + } + } + T FindLayer() where T : class { foreach (var layer in _layers)