Bläddra i källkod

Add Popup.ShouldUseOverlayLayer property (#5629)

* Implement Popup.ShouldUseOverlayLayer property

* Add ShouldUseOverlayLayer tests
Maxwell Katz 9 månader sedan
förälder
incheckning
98a388d3a3

+ 8 - 2
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@@ -194,10 +194,16 @@ namespace Avalonia.Controls.Primitives
         // TODO12: mark PrivateAPI or internal.
         [Unstable("PopupHost is considered an internal API. Use Popup or any Popup-based controls (Flyout, Tooltip) instead.")]
         public static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver)
+            => CreatePopupHost(target, dependencyResolver, false);
+
+        internal static IPopupHost CreatePopupHost(Visual target, IAvaloniaDependencyResolver? dependencyResolver, bool shouldUseOverlayLayer)
         {
-            if (TopLevel.GetTopLevel(target) is { } topLevel && topLevel.PlatformImpl?.CreatePopup() is { } popupImpl)
+            if (!shouldUseOverlayLayer)
             {
-                return new PopupRoot(topLevel, popupImpl, dependencyResolver);
+                if (TopLevel.GetTopLevel(target) is { } topLevel && topLevel.PlatformImpl?.CreatePopup() is { } popupImpl)
+                {
+                    return new PopupRoot(topLevel, popupImpl, dependencyResolver);
+                }
             }
 
             if (OverlayLayer.GetOverlayLayer(target) is { } overlayLayer)

+ 38 - 1
src/Avalonia.Controls/Primitives/Popup.cs

@@ -141,8 +141,21 @@ namespace Avalonia.Controls.Primitives
         public static readonly AttachedProperty<bool> TakesFocusFromNativeControlProperty =
             AvaloniaProperty.RegisterAttached<Popup, Control, bool>(nameof(TakesFocusFromNativeControl), true);
 
+        /// <summary>
+        /// Defines the <see cref="ShouldUseOverlayLayer"/> property.
+        /// </summary>
+        public static readonly StyledProperty<bool> ShouldUseOverlayLayerProperty =
+            AvaloniaProperty.Register<Popup, bool>(nameof(ShouldUseOverlayLayer));
+
+        /// <summary>
+        /// Defines the <see cref="IsUsingOverlayLayer"/> property.
+        /// </summary>
+        public static readonly DirectProperty<Popup, bool> IsUsingOverlayLayerProperty = AvaloniaProperty.RegisterDirect<Popup, bool>(
+            nameof(IsUsingOverlayLayer), o => o.IsUsingOverlayLayer);
+
         private bool _isOpenRequested;
         private bool _ignoreIsOpenChanged;
+        private bool _isUsingOverlayLayer;
         private PopupOpenState? _openState;
         private Action<IPopupHost?>? _popupHostChangedHandler;
 
@@ -386,6 +399,29 @@ namespace Avalonia.Controls.Primitives
             set => SetValue(TakesFocusFromNativeControlProperty, value);
         }
 
+        /// <summary>
+        /// Gets or sets a value that indicates whether the popup should be shown in the overlay layer of the parent window.
+        /// </summary>
+        /// <remarks>
+        /// When <see cref="ShouldUseOverlayLayer"/> is "false" implementation depends on the platform.
+        /// Use <see cref="IsUsingOverlayLayer"/> to get actual popup behavior.
+        /// This is an equvalent of `OverlayPopups` property of the platform options, but settable independently per each popup. 
+        /// </remarks>
+        public bool ShouldUseOverlayLayer
+        {
+            get => GetValue(ShouldUseOverlayLayerProperty);
+            set => SetValue(ShouldUseOverlayLayerProperty, value);
+        }
+
+        /// <summary>
+        /// Gets a value that indicates whether the popup is shown in the overlay layer of the parent window.
+        /// </summary>
+        public bool IsUsingOverlayLayer
+        {
+            get => _isUsingOverlayLayer;
+            private set => SetAndRaise(IsUsingOverlayLayerProperty, ref _isUsingOverlayLayer, value);
+        }
+
         IPopupHost? IPopupHostProvider.PopupHost => Host;
 
         event Action<IPopupHost?>? IPopupHostProvider.PopupHostChanged 
@@ -423,7 +459,7 @@ namespace Avalonia.Controls.Primitives
 
             _isOpenRequested = false;
 
-            var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
+            var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver, ShouldUseOverlayLayer);
             var handlerCleanup = new CompositeDisposable(7);
 
             UpdateHostSizing(popupHost, topLevel, placementTarget);
@@ -541,6 +577,7 @@ namespace Avalonia.Controls.Primitives
             WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint);
 
             popupHost.Show();
+            IsUsingOverlayLayer = popupHost is OverlayPopupHost;
 
             if (TakesFocusFromNativeControl)
                 popupHost.TakeFocus();

+ 32 - 0
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@@ -1260,6 +1260,38 @@ namespace Avalonia.Controls.UnitTests.Primitives
             return result;
         }
 
+        [Fact]
+        public void Popup_Open_With_Correct_IsUsingOverlayLayer_And_Disabled_OverlayLayer()
+        {
+            using (CreateServices())
+            {
+                var target = new Popup();
+                target.IsOpen = true;
+                target.ShouldUseOverlayLayer = false;
+
+                var window = PreparedWindow(target);
+                window.Show();
+
+                Assert.Equal(UsePopupHost, target.IsUsingOverlayLayer);
+            }
+        }
+
+        [Fact]
+        public void Popup_Open_With_Correct_IsUsingOverlayLayer_And_Enabled_OverlayLayer()
+        {
+            using (CreateServices())
+            {
+                var target = new Popup();
+                target.IsOpen = true;
+                target.ShouldUseOverlayLayer = true;
+
+                var window = PreparedWindow(target);
+                window.Show();
+
+                Assert.Equal(true, target.IsUsingOverlayLayer);
+            }
+        }
+
         private IDisposable CreateServices()
         {
             return UnitTestApplication.Start(TestServices.StyledWindow.With(