diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
index e96df62f2f..8d3d97b94f 100644
--- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs
+++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs
@@ -11,8 +11,6 @@ namespace Avalonia.Controls.Primitives
/// Represents a surface for showing adorners.
/// Adorners are always on top of the adorned element and are positioned to stay relative to the adorned element.
///
- ///
- ///
public class AdornerLayer : Canvas
{
///
diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs
index 4d3659c96d..3e9967f235 100644
--- a/src/Avalonia.Controls/Primitives/IPopupHost.cs
+++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs
@@ -13,7 +13,7 @@ namespace Avalonia.Controls.Primitives
///
/// A popup host can be either be a popup window created by the operating system
/// () or an which is created
- /// on an .
+ /// on an .
///
internal interface IPopupHost : IDisposable, IFocusScope
{
diff --git a/src/Avalonia.Controls/Primitives/OverlayLayer.cs b/src/Avalonia.Controls/Primitives/OverlayLayer.cs
index 6b1c611949..3337288a13 100644
--- a/src/Avalonia.Controls/Primitives/OverlayLayer.cs
+++ b/src/Avalonia.Controls/Primitives/OverlayLayer.cs
@@ -1,20 +1,34 @@
using System.Linq;
-using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
{
- internal class OverlayLayer : Canvas
+ ///
+ /// Represents a surface for showing overlays.
+ /// Overlays are displayed on top of other elements, but behind popups.
+ ///
+ public class OverlayLayer : Canvas
{
protected override bool BypassFlowDirectionPolicies => true;
+
public Size AvailableSize { get; private set; }
+
+ internal OverlayLayer()
+ {
+ }
+
+ ///
+ /// Retrieves the overlay layer associated with the specified visual, if any.
+ ///
+ /// The visual for which to retrieve the associated overlay layer.
+ /// The associated with the visual, or null if no overlay layer exists.
public static OverlayLayer? GetOverlayLayer(Visual visual)
{
- foreach(var v in visual.GetVisualAncestors())
- if(v is VisualLayerManager vlm)
- if (vlm.OverlayLayer != null)
- return vlm.OverlayLayer;
- if (TopLevel.GetTopLevel(visual) is {} tl)
+ foreach (var v in visual.GetVisualAncestors())
+ if (v is VisualLayerManager { OverlayLayer: { } layer })
+ return layer;
+
+ if (TopLevel.GetTopLevel(visual) is { } tl)
{
var layers = tl.GetVisualDescendants().OfType().FirstOrDefault();
return layers?.OverlayLayer;
@@ -23,13 +37,15 @@ namespace Avalonia.Controls.Primitives
return null;
}
+ ///
protected override Size MeasureOverride(Size availableSize)
{
- foreach (Control child in Children)
+ foreach (var child in Children)
child.Measure(availableSize);
return availableSize;
}
+ ///
protected override Size ArrangeOverride(Size finalSize)
{
// We are saving it here since child controls might need to know the entire size of the overlay
diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
index daab1d8539..4e46406dd3 100644
--- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
+++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
@@ -19,7 +19,7 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty TransformProperty =
PopupRoot.TransformProperty.AddOwner();
- private readonly OverlayLayer _overlayLayer;
+ private readonly PopupOverlayLayer _overlayLayer;
private readonly ManagedPopupPositioner _positioner;
private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler;
internal IKeyboardNavigationHandler Tests_KeyboardNavigationHandler => _keyboardNavigationHandler!;
@@ -31,7 +31,7 @@ namespace Avalonia.Controls.Primitives
static OverlayPopupHost()
=> KeyboardNavigation.TabNavigationProperty.OverrideDefaultValue(KeyboardNavigationMode.Cycle);
- internal OverlayPopupHost(OverlayLayer overlayLayer)
+ internal OverlayPopupHost(PopupOverlayLayer overlayLayer)
{
_overlayLayer = overlayLayer;
_positioner = new ManagedPopupPositioner(this);
@@ -161,7 +161,7 @@ namespace Avalonia.Controls.Primitives
}
}
- if (OverlayLayer.GetOverlayLayer(target) is { } overlayLayer)
+ if (PopupOverlayLayer.GetPopupOverlayLayer(target) is { } overlayLayer)
{
return new OverlayPopupHost(overlayLayer);
}
diff --git a/src/Avalonia.Controls/Primitives/PopupOverlayLayer.cs b/src/Avalonia.Controls/Primitives/PopupOverlayLayer.cs
new file mode 100644
index 0000000000..4ca54e3d8f
--- /dev/null
+++ b/src/Avalonia.Controls/Primitives/PopupOverlayLayer.cs
@@ -0,0 +1,42 @@
+using System.Linq;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Controls.Primitives
+{
+ internal sealed class PopupOverlayLayer : Canvas
+ {
+ protected override bool BypassFlowDirectionPolicies => true;
+
+ public Size AvailableSize { get; private set; }
+
+ public static PopupOverlayLayer? GetPopupOverlayLayer(Visual visual)
+ {
+ foreach (var v in visual.GetVisualAncestors())
+ if (v is VisualLayerManager { PopupOverlayLayer: { } layer })
+ return layer;
+
+ if (TopLevel.GetTopLevel(visual) is { } tl)
+ {
+ var layers = tl.GetVisualDescendants().OfType().FirstOrDefault();
+ return layers?.PopupOverlayLayer;
+ }
+
+ return null;
+ }
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ foreach (var child in Children)
+ child.Measure(availableSize);
+ return availableSize;
+ }
+
+ protected override Size ArrangeOverride(Size finalSize)
+ {
+ // We are saving it here since child controls might need to know the entire size of the overlay
+ // and Bounds won't be updated in time
+ AvailableSize = finalSize;
+ return base.ArrangeOverride(finalSize);
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Primitives/TextSelectorLayer.cs b/src/Avalonia.Controls/Primitives/TextSelectorLayer.cs
index fad7718917..d6b080e588 100644
--- a/src/Avalonia.Controls/Primitives/TextSelectorLayer.cs
+++ b/src/Avalonia.Controls/Primitives/TextSelectorLayer.cs
@@ -1,5 +1,4 @@
using System.Linq;
-using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives
@@ -7,14 +6,16 @@ namespace Avalonia.Controls.Primitives
public class TextSelectorLayer : Canvas
{
protected override bool BypassFlowDirectionPolicies => true;
+
public Size AvailableSize { get; private set; }
+
public static TextSelectorLayer? GetTextSelectorLayer(Visual visual)
{
foreach (var v in visual.GetVisualAncestors())
- if (v is VisualLayerManager vlm)
- if (vlm.TextSelectorLayer != null)
- return vlm.TextSelectorLayer;
- if (visual is TopLevel tl)
+ if (v is VisualLayerManager { TextSelectorLayer: { } textSelectorLayer })
+ return textSelectorLayer;
+
+ if (TopLevel.GetTopLevel(visual) is { } tl)
{
var layers = tl.GetVisualDescendants().OfType().FirstOrDefault();
return layers?.TextSelectorLayer;
@@ -25,7 +26,7 @@ namespace Avalonia.Controls.Primitives
protected override Size MeasureOverride(Size availableSize)
{
- foreach (Control child in Children)
+ foreach (var child in Children)
child.Measure(availableSize);
return default;
}
diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
index 932cd033a3..6630f1e09c 100644
--- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
+++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
@@ -6,9 +6,10 @@ namespace Avalonia.Controls.Primitives
public sealed class VisualLayerManager : Decorator
{
private const int AdornerZIndex = int.MaxValue - 100;
- private const int LightDismissOverlayZIndex = int.MaxValue - 98;
- private const int OverlayZIndex = int.MaxValue - 97;
- private const int TextSelectorLayerZIndex = int.MaxValue - 96;
+ private const int OverlayZIndex = int.MaxValue - 98;
+ private const int LightDismissOverlayZIndex = int.MaxValue - 97;
+ private const int PopupOverlayZIndex = int.MaxValue - 96;
+ private const int TextSelectorLayerZIndex = int.MaxValue - 95;
private ILogicalRoot? _logicalRoot;
private readonly List _layers = new();
@@ -26,6 +27,19 @@ namespace Avalonia.Controls.Primitives
}
}
+ internal PopupOverlayLayer? PopupOverlayLayer
+ {
+ get
+ {
+ if (IsPopup)
+ return null;
+ var rv = FindLayer();
+ if (rv == null)
+ AddLayer(rv = new PopupOverlayLayer(), PopupOverlayZIndex);
+ return rv;
+ }
+ }
+
internal OverlayLayer? OverlayLayer
{
get
diff --git a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
index 8672013b81..ad0662311b 100644
--- a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Controls.UnitTests
_toolTipOpenSubscription = ToolTip.IsOpenProperty.Changed.Subscribe(new AnonymousObserver>(e =>
{
if (e.Sender is Visual visual && TopLevel.GetTopLevel(visual) is {} root)
- OverlayLayer.GetOverlayLayer(visual)?.Measure(root.ClientSize);
+ PopupOverlayLayer.GetPopupOverlayLayer(visual)?.Measure(root.ClientSize);
}));
}