From acab1102081e3d09c359b79174e8706ac127f34b Mon Sep 17 00:00:00 2001 From: amwx Date: Tue, 8 Sep 2020 16:57:38 -0500 Subject: [PATCH 1/5] PopupRoot IFocusScope --- src/Avalonia.Controls/Primitives/PopupRoot.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index da7352b77f..2721ab879f 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Platform; @@ -14,7 +15,7 @@ namespace Avalonia.Controls.Primitives /// /// The root window of a . /// - public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost + public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost, IFocusScope { private readonly TopLevel _parent; private PopupPositionerParameters _positionerParameters; From 4e2dc7f5dbb9a2c38ce9e24a75318b5b32b38758 Mon Sep 17 00:00:00 2001 From: amwx Date: Tue, 8 Sep 2020 17:41:25 -0500 Subject: [PATCH 2/5] Add test --- .../Primitives/PopupTests.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index d9176ca55d..a0b7368a4e 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -395,6 +395,34 @@ namespace Avalonia.Controls.UnitTests.Primitives } } + [Fact] + public void Focusable_Controls_In_Popup_Should_Get_Focus() + { + using (CreateServicesWithFocus()) + { + var tb = new TextBox(); + var b = new Button(); + var p = new Popup + { + PlacementTarget = PreparedWindow(), + Child = new StackPanel + { + Children = + { + tb, + b + } + } + }; + ((ISetLogicalParent)p).SetParent(p.PlacementTarget); + + p.Open(); + tb.Focus(); + + Assert.True(FocusManager.Instance?.Current == tb); + } + } + private IDisposable CreateServices() { return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: @@ -407,6 +435,21 @@ namespace Avalonia.Controls.UnitTests.Primitives }))); } + 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())); + } + + private PointerPressedEventArgs CreatePointerPressedEventArgs(Window source, Point p) { var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); From 05c6978617d87c30ae7cfb6a541c397b1547e400 Mon Sep 17 00:00:00 2001 From: amwx Date: Thu, 10 Sep 2020 16:29:16 -0500 Subject: [PATCH 3/5] Try to get test to pass on CI --- tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index a0b7368a4e..e5dcba9912 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -416,8 +416,12 @@ namespace Avalonia.Controls.UnitTests.Primitives }; ((ISetLogicalParent)p).SetParent(p.PlacementTarget); + p.Opened += (s, e) => + { + tb.Focus(); + }; + p.Open(); - tb.Focus(); Assert.True(FocusManager.Instance?.Current == tb); } From 6c52a7cb64ca660d1b03abfca22918e0a4704199 Mon Sep 17 00:00:00 2001 From: amwx Date: Wed, 23 Sep 2020 16:16:09 -0500 Subject: [PATCH 4/5] Move IFocusScope to IPopupHost --- src/Avalonia.Controls/Primitives/IPopupHost.cs | 3 ++- src/Avalonia.Controls/Primitives/PopupRoot.cs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index e424bf683d..82a49c4189 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Input; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -13,7 +14,7 @@ namespace Avalonia.Controls.Primitives /// () or an which is created /// on an . /// - public interface IPopupHost : IDisposable + public interface IPopupHost : IDisposable, IFocusScope { /// /// Sets the control to display in the popup. diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 2721ab879f..da7352b77f 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; using Avalonia.Controls.Primitives.PopupPositioning; -using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Platform; @@ -15,7 +14,7 @@ namespace Avalonia.Controls.Primitives /// /// The root window of a . /// - public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost, IFocusScope + public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost { private readonly TopLevel _parent; private PopupPositionerParameters _positionerParameters; From 52075d6cd98527d7eb75c890c3d1f0a91fe6e203 Mon Sep 17 00:00:00 2001 From: amwx Date: Wed, 23 Sep 2020 16:16:54 -0500 Subject: [PATCH 5/5] Fix Test --- .../Primitives/PopupTests.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index e5dcba9912..53a8db2176 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -400,11 +400,13 @@ namespace Avalonia.Controls.UnitTests.Primitives { using (CreateServicesWithFocus()) { + var window = PreparedWindow(); + var tb = new TextBox(); var b = new Button(); var p = new Popup { - PlacementTarget = PreparedWindow(), + PlacementTarget = window, Child = new StackPanel { Children = @@ -415,15 +417,28 @@ namespace Avalonia.Controls.UnitTests.Primitives } }; ((ISetLogicalParent)p).SetParent(p.PlacementTarget); + window.Show(); - p.Opened += (s, e) => + p.Open(); + + if(p.Host is OverlayPopupHost host) { - tb.Focus(); - }; + //Need to measure/arrange for visual children to show up + //in OverlayPopupHost + host.Measure(Size.Infinity); + host.Arrange(new Rect(host.DesiredSize)); + } - p.Open(); + tb.Focus(); Assert.True(FocusManager.Instance?.Current == tb); + + //Ensure focus remains in the popup + var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next); + + Assert.True(nextFocus == b); + + p.Close(); } }