From 1d0ac58755f68a973913e8a17387f2d86616411b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 19:54:51 +0100 Subject: [PATCH 01/13] add repro for popup transparency pointer event issue. --- .../ControlCatalog/Pages/CheckBoxPage.xaml | 43 +++++++++---------- .../ControlCatalog/Pages/CheckBoxPage.xaml.cs | 15 ++++++- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml b/samples/ControlCatalog/Pages/CheckBoxPage.xaml index 2f60fc5dae..78a8ff5550 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml @@ -1,28 +1,25 @@ - - A check box control + x:Class="ControlCatalog.Pages.CheckBoxPage" Background="White"> + + + + + + + + - - - _Unchecked - _Checked - _Indeterminate - Disabled - - - Three State: Unchecked - Three State: Checked - Three State: Indeterminate - Three State: Disabled + + + + + + Popup Text + + + - - + + diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs index 5027c94f5e..c3066c55f9 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -1,18 +1,31 @@ using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public class CheckBoxPage : UserControl + public partial class CheckBoxPage : UserControl { + private TextBlock myTb; + public CheckBoxPage() { this.InitializeComponent(); + + myTb = this.FindControl("myTb"); + } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + myTb.Text = e.GetPosition(this).ToString(); + } } } From a9e16dc89069f6baa5b4aa24fec3af942c542a2a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 20:00:06 +0100 Subject: [PATCH 02/13] update repro --- samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs | 3 ++- src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs index c3066c55f9..60e75561e1 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -7,6 +7,7 @@ namespace ControlCatalog.Pages public partial class CheckBoxPage : UserControl { private TextBlock myTb; + private int count; public CheckBoxPage() { @@ -25,7 +26,7 @@ namespace ControlCatalog.Pages { base.OnPointerMoved(e); - myTb.Text = e.GetPosition(this).ToString(); + myTb.Text = e.GetPosition(this).ToString() + ", " + count++; } } } diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 5a924830b1..16be9630c1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -13,6 +13,7 @@ + + From 7aa531530be53bd70c6a33d64d951097838e6593 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 20:25:55 +0100 Subject: [PATCH 03/13] implent popup routing events to parent toplevel. --- src/Avalonia.Base/Input/PointerEventArgs.cs | 11 ++++++++++- .../Controls/PopupRoot.xaml | 18 +++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 7f82199b56..0556d8702f 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -70,7 +70,16 @@ namespace Avalonia.Input if (relativeTo == null) return pt; - return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; + if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot)) + { + var screenPt = _rootVisual.PointToScreen(pt); + + return relativeTo.PointToClient(screenPt); + } + else + { + return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; + } } /// diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 16be9630c1..567c9b72e7 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -13,15 +13,15 @@ - - - - - + + + + + From d064ad4e6ab1dd12638b1b249953403a0a574512 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 20:34:04 +0100 Subject: [PATCH 04/13] transform to visual. --- src/Avalonia.Base/Input/PointerEventArgs.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 0556d8702f..a53791e414 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -70,16 +70,14 @@ namespace Avalonia.Input if (relativeTo == null) return pt; - if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot)) + if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is Visual v) { var screenPt = _rootVisual.PointToScreen(pt); - return relativeTo.PointToClient(screenPt); - } - else - { - return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; + return relativeTo.PointToClient(screenPt) * v.TransformToVisual(relativeTo) ?? default; } + + return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; } /// From ba8ddd04189f95ec35711019d1c90f5c605a649b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 20:37:14 +0100 Subject: [PATCH 05/13] no need for transformtovisual. --- src/Avalonia.Base/Input/PointerEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index a53791e414..5db9266d06 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -74,7 +74,7 @@ namespace Avalonia.Input { var screenPt = _rootVisual.PointToScreen(pt); - return relativeTo.PointToClient(screenPt) * v.TransformToVisual(relativeTo) ?? default; + return relativeTo.PointToClient(screenPt); } return pt * _rootVisual.TransformToVisual(relativeTo) ?? default; From d24903957eb27ca89670c28e5f4d34b8e123e58f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 23 May 2023 20:40:25 +0100 Subject: [PATCH 06/13] revert control catalog changes. --- .../ControlCatalog/Pages/CheckBoxPage.xaml | 43 ++++++++++--------- .../ControlCatalog/Pages/CheckBoxPage.xaml.cs | 16 +------ 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml b/samples/ControlCatalog/Pages/CheckBoxPage.xaml index 78a8ff5550..2f60fc5dae 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml @@ -1,25 +1,28 @@ - - - - - - - - + x:Class="ControlCatalog.Pages.CheckBoxPage"> + + A check box control - - - - - - Popup Text - - - + + + _Unchecked + _Checked + _Indeterminate + Disabled + + + Three State: Unchecked + Three State: Checked + Three State: Indeterminate + Three State: Disabled - - + + diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs index 60e75561e1..5027c94f5e 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -1,32 +1,18 @@ using Avalonia.Controls; -using Avalonia.Input; using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public partial class CheckBoxPage : UserControl + public class CheckBoxPage : UserControl { - private TextBlock myTb; - private int count; - public CheckBoxPage() { this.InitializeComponent(); - - myTb = this.FindControl("myTb"); - } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } - - protected override void OnPointerMoved(PointerEventArgs e) - { - base.OnPointerMoved(e); - - myTb.Text = e.GetPosition(this).ToString() + ", " + count++; - } } } From 10b89ac13a3d7ac24a598b37427bd6c8ac65dd10 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 24 May 2023 11:19:15 +0100 Subject: [PATCH 07/13] Revert "revert control catalog changes." This reverts commit d24903957eb27ca89670c28e5f4d34b8e123e58f. --- .../ControlCatalog/Pages/CheckBoxPage.xaml | 43 +++++++++---------- .../ControlCatalog/Pages/CheckBoxPage.xaml.cs | 16 ++++++- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml b/samples/ControlCatalog/Pages/CheckBoxPage.xaml index 2f60fc5dae..78a8ff5550 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml @@ -1,28 +1,25 @@ - - A check box control + x:Class="ControlCatalog.Pages.CheckBoxPage" Background="White"> + + + + + + + + - - - _Unchecked - _Checked - _Indeterminate - Disabled - - - Three State: Unchecked - Three State: Checked - Three State: Indeterminate - Three State: Disabled + + + + + + Popup Text + + + - - + + diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs index 5027c94f5e..60e75561e1 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -1,18 +1,32 @@ using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public class CheckBoxPage : UserControl + public partial class CheckBoxPage : UserControl { + private TextBlock myTb; + private int count; + public CheckBoxPage() { this.InitializeComponent(); + + myTb = this.FindControl("myTb"); + } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } + + protected override void OnPointerMoved(PointerEventArgs e) + { + base.OnPointerMoved(e); + + myTb.Text = e.GetPosition(this).ToString() + ", " + count++; + } } } From 215707ba7e30ad709d4d9adae2950376c7c9ee8a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 May 2023 19:44:52 +0100 Subject: [PATCH 08/13] add comments explaining and remove unnecessary condition. --- src/Avalonia.Base/Input/PointerEventArgs.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 5db9266d06..383c807a73 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -70,10 +70,14 @@ namespace Avalonia.Input if (relativeTo == null) return pt; - if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is Visual v) + // If the visual the user passed in, is not connected to the same visual root + // (i.e. they called it for a control inside a popup. + if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot)) { + // Convert to absolute screen coordinates. var screenPt = _rootVisual.PointToScreen(pt); + // Convert to client co-ordinates of the visual inside the other visual root. return relativeTo.PointToClient(screenPt); } From b3869981701114356fbb1b2fac6fdcbbf7757250 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 May 2023 19:45:06 +0100 Subject: [PATCH 09/13] remove template change. --- src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 567c9b72e7..87e954b630 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -13,7 +13,6 @@ - - From b7568940c82848621bdcffa9b37af6665f75c821 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 May 2023 19:48:20 +0100 Subject: [PATCH 10/13] revert changes. --- .../ControlCatalog/Pages/CheckBoxPage.xaml | 43 ++++++++++--------- .../ControlCatalog/Pages/CheckBoxPage.xaml.cs | 16 +------ .../Controls/PopupRoot.xaml | 14 +++--- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml b/samples/ControlCatalog/Pages/CheckBoxPage.xaml index 78a8ff5550..2f60fc5dae 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml @@ -1,25 +1,28 @@ - - - - - - - - + x:Class="ControlCatalog.Pages.CheckBoxPage"> + + A check box control - - - - - - Popup Text - - - + + + _Unchecked + _Checked + _Indeterminate + Disabled + + + Three State: Unchecked + Three State: Checked + Three State: Indeterminate + Three State: Disabled - - + + diff --git a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs index 60e75561e1..5027c94f5e 100644 --- a/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs +++ b/samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs @@ -1,32 +1,18 @@ using Avalonia.Controls; -using Avalonia.Input; using Avalonia.Markup.Xaml; namespace ControlCatalog.Pages { - public partial class CheckBoxPage : UserControl + public class CheckBoxPage : UserControl { - private TextBlock myTb; - private int count; - public CheckBoxPage() { this.InitializeComponent(); - - myTb = this.FindControl("myTb"); - } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } - - protected override void OnPointerMoved(PointerEventArgs e) - { - base.OnPointerMoved(e); - - myTb.Text = e.GetPosition(this).ToString() + ", " + count++; - } } } diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 87e954b630..5a924830b1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -13,13 +13,13 @@ - - - + + + From e2511344b8225817381db31af3c7e08f9a3ba4b0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 25 May 2023 20:37:37 +0100 Subject: [PATCH 11/13] fix tests. --- src/Avalonia.Base/Input/PointerEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs index 383c807a73..9c24e5c314 100644 --- a/src/Avalonia.Base/Input/PointerEventArgs.cs +++ b/src/Avalonia.Base/Input/PointerEventArgs.cs @@ -72,7 +72,7 @@ namespace Avalonia.Input // If the visual the user passed in, is not connected to the same visual root // (i.e. they called it for a control inside a popup. - if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot)) + if (!ReferenceEquals(_rootVisual, relativeTo.VisualRoot) && relativeTo.VisualRoot is { }) { // Convert to absolute screen coordinates. var screenPt = _rootVisual.PointToScreen(pt); From a78c41dd41a8dc699dbc64dafe5b535f62f5062c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 26 May 2023 11:20:01 +0100 Subject: [PATCH 12/13] add a unit test. --- .../Primitives/PopupTests.cs | 70 ++++++++++++++++++- .../MockWindowingPlatform.cs | 17 +++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index 51399d1202..e307a80441 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -19,6 +19,7 @@ using Avalonia.Rendering; using System.Threading.Tasks; using Avalonia.Threading; using Avalonia.Interactivity; +using Avalonia.Media; namespace Avalonia.Controls.UnitTests.Primitives { @@ -1076,10 +1077,77 @@ namespace Avalonia.Controls.UnitTests.Primitives } } + [Fact] + public void GetPosition_On_Control_In_Popup_Called_From_Parent_Should_Return_Valid_Coordinates() + { + using (CreateServices()) + { + var popupContent = new Border() { Height = 100, Width = 100, Background = Brushes.Red }; + var popup = new Popup { Child = popupContent, HorizontalOffset = 40, VerticalOffset = 40, Placement = PlacementMode.AnchorAndGravity, + PlacementAnchor = PopupAnchor.TopLeft, PlacementGravity = PopupGravity.BottomRight}; + var popupParent = new Border { Child = popup }; + var root = PreparedWindow(popupParent); + + var raised = 0; + + root.LayoutManager.ExecuteInitialLayoutPass(); + popup.Open(); + root.LayoutManager.ExecuteLayoutPass(); + + var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); + + var pts = popupContent.PointToScreen(new Point(10, 10)); + + Assert.Equal(new PixelPoint(50, 50), pts); + + var ev = new PointerPressedEventArgs( + popupContent, + pointer, + popupContent.VisualRoot as PopupRoot, + new Point(50 , 50), + 0, + new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), + KeyModifiers.None); + + Point pointRelativeToWindowContent = default; + + popupParent.AddHandler(Button.PointerPressedEvent, (s, e) => + { + ++raised; + + pointRelativeToWindowContent = e.GetPosition(popupParent); + }); + + popupContent.RaiseEvent(ev); + + Assert.Equal(1, raised); + Assert.Equal(new Point(90, 90), pointRelativeToWindowContent); + } + } + + private static PopupRoot CreateRoot(TopLevel popupParent, IPopupImpl impl = null) + { + impl ??= popupParent.PlatformImpl.CreatePopup(); + + var result = new PopupRoot(popupParent, impl) + { + Template = new FuncControlTemplate((parent, scope) => + new ContentPresenter + { + Name = "PART_ContentPresenter", + [!ContentPresenter.ContentProperty] = parent[!PopupRoot.ContentProperty], + }.RegisterInNameScope(scope)), + }; + + result.ApplyTemplate(); + + return result; + } + private IDisposable CreateServices() { return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: - new MockWindowingPlatform(null, + new MockWindowingPlatform(() => MockWindowingPlatform.CreateWindowMock().Object, x => { if(UsePopupHost) diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index ca71a97a6e..65612e45a0 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -55,6 +55,12 @@ namespace Avalonia.UnitTests windowImpl.Object.PositionChanged?.Invoke(x); }); + windowImpl.Setup(x => x.PointToScreen(It.IsAny())) + .Returns((point) => PixelPoint.FromPoint(point, 1) + position); + + windowImpl.Setup(x => x.PointToClient(It.IsAny())) + .Returns(point => (point - position).ToPoint(1)); + windowImpl.Setup(x => x.Resize(It.IsAny(), It.IsAny())) .Callback((x, y) => { @@ -83,10 +89,12 @@ namespace Avalonia.UnitTests { var popupImpl = new Mock(); var clientSize = new Size(); + var position = new PixelPoint(); var positionerHelper = new ManagedPopupPositionerPopupImplHelper(parent, (pos, size, scale) => { clientSize = size.Constrain(s_screenSize); + position = pos; popupImpl.Object.PositionChanged?.Invoke(pos); popupImpl.Object.Resized?.Invoke(clientSize, WindowResizeReason.Unspecified); }); @@ -100,6 +108,15 @@ namespace Avalonia.UnitTests popupImpl.Setup(x => x.MaxAutoSizeHint).Returns(s_screenSize); popupImpl.Setup(x => x.RenderScaling).Returns(1); popupImpl.Setup(x => x.PopupPositioner).Returns(positioner); + popupImpl.Setup(x => x.Position).Returns(()=>position); + + popupImpl.Setup(x => x.PointToScreen(It.IsAny())) + .Returns((point) => PixelPoint.FromPoint(point, 1) + position); + + popupImpl.Setup(x => x.PointToClient(It.IsAny())) + .Returns(point => (point - position).ToPoint(1)); + + popupImpl.Setup(r => r.TryGetFeature(It.IsAny())).Returns(null); popupImpl.Setup(x => x.Dispose()).Callback(() => From 40515fc79846e68a3615959418ba6cc55ca15f9a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 26 May 2023 14:49:45 +0200 Subject: [PATCH 13/13] Clarify/fix unit test. Now all of the other popup tests pass as well. --- .../Primitives/PopupTests.cs | 82 +++++++++++-------- .../MockWindowingPlatform.cs | 11 +-- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index e307a80441..888162cfee 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -1080,6 +1080,10 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void GetPosition_On_Control_In_Popup_Called_From_Parent_Should_Return_Valid_Coordinates() { + // This test only applies when using a PopupRoot host and not an overlay popup. + if (UsePopupHost) + return; + using (CreateServices()) { var popupContent = new Border() { Height = 100, Width = 100, Background = Brushes.Red }; @@ -1088,18 +1092,18 @@ namespace Avalonia.Controls.UnitTests.Primitives var popupParent = new Border { Child = popup }; var root = PreparedWindow(popupParent); - var raised = 0; - - root.LayoutManager.ExecuteInitialLayoutPass(); popup.Open(); - root.LayoutManager.ExecuteLayoutPass(); - - var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); - var pts = popupContent.PointToScreen(new Point(10, 10)); + // Verify that the popup is positioned at 40,40 as descibed by the Horizontal/ + // VerticalOffset: 10,10 becomes 50,50 in screen coordinates. + Assert.Equal(new PixelPoint(50, 50), popupContent.PointToScreen(new Point(10, 10))); - Assert.Equal(new PixelPoint(50, 50), pts); + // The popup parent is positioned at 0,0 in screen coordinates so client and + // screen coordinates are the same. + Assert.Equal(new PixelPoint(10, 10), popupParent.PointToScreen(new Point(10, 10))); + // The event will be raised on the popup content at 50,50 (90,90 in screen coordinates) + var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); var ev = new PointerPressedEventArgs( popupContent, pointer, @@ -1109,19 +1113,28 @@ namespace Avalonia.Controls.UnitTests.Primitives new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed), KeyModifiers.None); - Point pointRelativeToWindowContent = default; + var contentRaised = 0; + var parentRaised = 0; - popupParent.AddHandler(Button.PointerPressedEvent, (s, e) => + // The event is raised on the popup content in popup coordinates. + popupContent.AddHandler(Button.PointerPressedEvent, (s, e) => { - ++raised; + ++contentRaised; + Assert.Equal(new Point(50, 50), e.GetPosition(popupContent)); + }); - pointRelativeToWindowContent = e.GetPosition(popupParent); + // The event is raised on the parent in root coordinates (which in this case are + // the same as screen coordinates). + popupParent.AddHandler(Button.PointerPressedEvent, (s, e) => + { + ++parentRaised; + Assert.Equal(new Point(90, 90), e.GetPosition(popupParent)); }); popupContent.RaiseEvent(ev); - Assert.Equal(1, raised); - Assert.Equal(new Point(90, 90), pointRelativeToWindowContent); + Assert.Equal(1, contentRaised); + Assert.Equal(1, parentRaised); } } @@ -1146,28 +1159,16 @@ namespace Avalonia.Controls.UnitTests.Primitives private IDisposable CreateServices() { - return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: - new MockWindowingPlatform(() => MockWindowingPlatform.CreateWindowMock().Object, - x => - { - if(UsePopupHost) - return null; - return MockWindowingPlatform.CreatePopupMock(x).Object; - }))); + return UnitTestApplication.Start(TestServices.StyledWindow.With( + windowingPlatform: CreateMockWindowingPlatform())); } private IDisposable CreateServicesWithFocus() { - return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: - new MockWindowingPlatform(null, - x => - { - if (UsePopupHost) - return null; - return MockWindowingPlatform.CreatePopupMock(x).Object; - }), - focusManager: new FocusManager(), - keyboardDevice: () => new KeyboardDevice())); + return UnitTestApplication.Start(TestServices.StyledWindow.With( + windowingPlatform: CreateMockWindowingPlatform(), + focusManager: new FocusManager(), + keyboardDevice: () => new KeyboardDevice())); } @@ -1184,6 +1185,23 @@ namespace Avalonia.Controls.UnitTests.Primitives KeyModifiers.None); } + private MockWindowingPlatform CreateMockWindowingPlatform() + { + return new MockWindowingPlatform(() => + { + var mock = MockWindowingPlatform.CreateWindowMock(); + + mock.Setup(x => x.CreatePopup()).Returns(() => + { + if (UsePopupHost) + return null; + return MockWindowingPlatform.CreatePopupMock(mock.Object).Object; + }); + + return mock.Object; + }, null); + } + private static Window PreparedWindow(object content = null) { var w = new Window { Content = content }; diff --git a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs index 65612e45a0..5451773da4 100644 --- a/tests/Avalonia.UnitTests/MockWindowingPlatform.cs +++ b/tests/Avalonia.UnitTests/MockWindowingPlatform.cs @@ -24,7 +24,6 @@ namespace Avalonia.UnitTests public static Mock CreateWindowMock(double initialWidth = 800, double initialHeight = 600) { var windowImpl = new Mock(); - var position = new PixelPoint(); var clientSize = new Size(initialWidth, initialHeight); windowImpl.SetupAllProperties(); @@ -35,7 +34,6 @@ namespace Avalonia.UnitTests windowImpl.Setup(x => x.DesktopScaling).Returns(1); windowImpl.Setup(x => x.RenderScaling).Returns(1); windowImpl.Setup(x => x.Screen).Returns(CreateScreenMock().Object); - windowImpl.Setup(x => x.Position).Returns(() => position); windowImpl.Setup(r => r.TryGetFeature(It.IsAny())).Returns(null); @@ -51,15 +49,15 @@ namespace Avalonia.UnitTests windowImpl.Setup(x => x.Move(It.IsAny())).Callback(x => { - position = x; + windowImpl.Setup(x => x.Position).Returns(x); windowImpl.Object.PositionChanged?.Invoke(x); }); windowImpl.Setup(x => x.PointToScreen(It.IsAny())) - .Returns((point) => PixelPoint.FromPoint(point, 1) + position); + .Returns((point) => PixelPoint.FromPoint(point, 1) + windowImpl.Object.Position); windowImpl.Setup(x => x.PointToClient(It.IsAny())) - .Returns(point => (point - position).ToPoint(1)); + .Returns(point => (point - windowImpl.Object.Position).ToPoint(1)); windowImpl.Setup(x => x.Resize(It.IsAny(), It.IsAny())) .Callback((x, y) => @@ -79,9 +77,6 @@ namespace Avalonia.UnitTests windowImpl.Object.Activated?.Invoke(); }); - windowImpl.Setup(x => x.PointToScreen(It.IsAny())) - .Returns((Point p) => PixelPoint.FromPoint(p, 1D) + position); - return windowImpl; }