Ver código fonte

Fixed positioning of popups attached to adorners (#14439)

Co-authored-by: Max Katz <[email protected]>
Bartosz Korczyński 1 ano atrás
pai
commit
4a1e2411cd

+ 29 - 1
src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs

@@ -468,7 +468,16 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
             {
                 if (target == null)
                     throw new InvalidOperationException("Placement mode is not Pointer and PlacementTarget is null");
-                var matrix = target.TransformToVisual(topLevel);
+                Matrix? matrix;
+                if (TryGetAdorner(target, out var adorned, out var adornerLayer))
+                {
+                    matrix = adorned!.TransformToVisual(topLevel) * target.TransformToVisual(adornerLayer!);
+                }
+                else
+                {
+                    matrix = target.TransformToVisual(topLevel);
+                }
+
                 if (matrix == null)
                 {
                     if (target.GetVisualRoot() == null)
@@ -529,6 +538,25 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
                 }
             }
         }
+
+        private static bool TryGetAdorner(Visual target, out Visual? adorned, out Visual? adornerLayer)
+        {
+            var element = target;
+            while (element != null)
+            {
+                if (AdornerLayer.GetAdornedElement(element) is { } adornedElement)
+                {
+                    adorned = adornedElement;
+                    adornerLayer = AdornerLayer.GetAdornerLayer(adorned);
+                    return true;
+                }
+                element = element.VisualParent;
+            }
+
+            adorned = null;
+            adornerLayer = null;
+            return false;
+        }
     }
 
 }

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

@@ -1143,6 +1143,53 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
 
+        [Fact]
+        public void Popup_Attached_To_Adorner_Respects_Adorner_Position()
+        {
+            using (CreateServices())
+            {
+                var popupTarget = new Border() { Height = 30, Background = Brushes.Red, [DockPanel.DockProperty] = Dock.Top };
+                var popupContent = new Border() { Height = 30, Width = 50, Background = Brushes.Yellow };
+                var popup = new Popup
+                {
+                    Child = popupContent,
+                    Placement = PlacementMode.AnchorAndGravity,
+                    PlacementTarget = popupTarget,
+                    PlacementAnchor = PopupAnchor.BottomRight,
+                    PlacementGravity = PopupGravity.BottomRight
+                };
+                var adorner = new DockPanel() { Children = { popupTarget, popup },
+                    HorizontalAlignment = HorizontalAlignment.Left,
+                    Width = 40,
+                    Margin = new Thickness(50, 5, 0, 0) };
+
+                var adorned = new Border() {
+                    Width = 100,
+                    Height = 100,
+                    Background = Brushes.Blue,
+                    [Canvas.LeftProperty] = 20,
+                    [Canvas.TopProperty] = 40
+                };
+                var windowContent = new Canvas();
+                windowContent.Children.Add(adorned);
+
+                var root = PreparedWindow(windowContent);
+
+                var adornerLayer = AdornerLayer.GetAdornerLayer(adorned);
+                adornerLayer.Children.Add(adorner);
+                AdornerLayer.SetAdornedElement(adorner, adorned);
+
+                root.LayoutManager.ExecuteInitialLayoutPass();
+                popup.Open();
+                Dispatcher.UIThread.RunJobs(DispatcherPriority.AfterRender);
+
+                // X: Adorned Canvas.Left + Adorner Margin Left + Adorner Width
+                // Y: Adorned Canvas.Top + Adorner Margin Top + Adorner Height
+                Assert.Equal(new PixelPoint(110, 75), popupContent.PointToScreen(new Point(0, 0)));
+            }
+        }
+
+
         private static PopupRoot CreateRoot(TopLevel popupParent, IPopupImpl impl = null)
         {
             impl ??= popupParent.PlatformImpl.CreatePopup();