From 109a5729edad4c5cca8cf7ec38c1af87ebff8513 Mon Sep 17 00:00:00 2001 From: Martin Zikmund Date: Tue, 7 Mar 2023 15:26:55 +0100 Subject: [PATCH] fix: Enable When_VerifyEnabledXYKeyboardNavigation in Uno Islands --- .../Repeater/RepeaterFocusTests.cs | 7 + .../Given_UnoFocusInputHandler.cs | 2 +- .../UI/Xaml/Input/FocusManager.mux.static.cs | 14 +- .../Input/Internal/UnoFocusInputHandler.cs | 181 +++++++++--------- src/Uno.UI/UI/Xaml/Internal/VisualTree.cs | 10 +- src/Uno.UI/UI/Xaml/UIElement.mux.cs | 2 +- 6 files changed, 108 insertions(+), 108 deletions(-) diff --git a/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/Repeater/RepeaterFocusTests.cs b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/Repeater/RepeaterFocusTests.cs index 06e849d7e8..a437ea1ce1 100644 --- a/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/Repeater/RepeaterFocusTests.cs +++ b/src/Uno.UI.RuntimeTests/MUX/Microsoft_UI_Xaml_Controls/Repeater/RepeaterFocusTests.cs @@ -50,6 +50,13 @@ namespace Windows.UI.Xaml.Tests.MUXControls.ApiTests.RepeaterTests return; } +#if HAS_UNO + if (TestServices.WindowHelper.IsXamlIsland) + { + return; + } +#endif + ItemsRepeater repeater = null; ScrollViewer scrollViewer = null; var data = new ObservableCollection(Enumerable.Range(0, 50).Select(i => "Item #" + i)); diff --git a/src/Uno.UI.RuntimeTests/Tests/Uno_UI_Xaml_Input/Given_UnoFocusInputHandler.cs b/src/Uno.UI.RuntimeTests/Tests/Uno_UI_Xaml_Input/Given_UnoFocusInputHandler.cs index eefdee16f0..a523c5879d 100644 --- a/src/Uno.UI.RuntimeTests/Tests/Uno_UI_Xaml_Input/Given_UnoFocusInputHandler.cs +++ b/src/Uno.UI.RuntimeTests/Tests/Uno_UI_Xaml_Input/Given_UnoFocusInputHandler.cs @@ -47,7 +47,7 @@ public class Given_UnoFocusInputHandler centerButton.Focus(FocusState.Programmatic); - var inputHandler = new UnoFocusInputHandler(VisualTree.GetRootForElement(centerButton)); + var inputHandler = new UnoFocusInputHandler(VisualTree.GetRootOrIslandForElement(centerButton)); var result = inputHandler.TryHandleDirectionalFocus(key); Assert.AreEqual(shouldSucceed, result); diff --git a/src/Uno.UI/UI/Xaml/Input/FocusManager.mux.static.cs b/src/Uno.UI/UI/Xaml/Input/FocusManager.mux.static.cs index 68ff98b15e..806c016895 100644 --- a/src/Uno.UI/UI/Xaml/Input/FocusManager.mux.static.cs +++ b/src/Uno.UI/UI/Xaml/Input/FocusManager.mux.static.cs @@ -37,12 +37,8 @@ namespace Windows.UI.Xaml.Input } } - private static bool InIslandsMode() - { - //TODO Uno: Islands mode should be applied for "WinUI" mode as it limits some APIs for - //multi-window use. For now choosing not limiting. - return false; - } + // TODO Uno: This should probably apply to multi-window as well #8341. + private static bool InIslandsMode() => CoreServices.Instance.InitializationType == InitializationType.IslandsOnly; private static object? FindNextFocus( FocusNavigationDirection focusNavigationDirection, @@ -54,7 +50,7 @@ namespace Windows.UI.Xaml.Input } var core = DXamlCore.Current; - if (core == null) + if (core is null) { throw new InvalidOperationException("XamlCore is not set."); } @@ -77,7 +73,7 @@ namespace Windows.UI.Xaml.Input var contentRootCoordinator = core.GetHandle().ContentRootCoordinator; var contentRoot = contentRootCoordinator?.CoreWindowContentRoot; - if (contentRoot == null) + if (contentRoot is null) { return null; } @@ -85,7 +81,7 @@ namespace Windows.UI.Xaml.Input focusManager = contentRoot.FocusManager; } - if (focusManager == null) + if (focusManager is null) { return null; } diff --git a/src/Uno.UI/UI/Xaml/Input/Internal/UnoFocusInputHandler.cs b/src/Uno.UI/UI/Xaml/Input/Internal/UnoFocusInputHandler.cs index 9c739520ec..5cdc073d4e 100644 --- a/src/Uno.UI/UI/Xaml/Input/Internal/UnoFocusInputHandler.cs +++ b/src/Uno.UI/UI/Xaml/Input/Internal/UnoFocusInputHandler.cs @@ -5,127 +5,126 @@ using Windows.System; using Windows.UI.Xaml; using Windows.UI.Xaml.Input; -namespace Uno.UI.Xaml.Input +namespace Uno.UI.Xaml.Input; + +internal class UnoFocusInputHandler { - internal class UnoFocusInputHandler - { - private readonly RootVisual _rootVisual; + private readonly UIElement _rootElement; + + private bool _isShiftDown; - private bool _isShiftDown; + public UnoFocusInputHandler(UIElement rootElement) + { + _rootElement = rootElement; + _rootElement.KeyDown += OnKeyDown; + _rootElement.KeyUp += OnKeyUp; + } - public UnoFocusInputHandler(RootVisual rootVisual) + private void OnKeyUp(object sender, KeyRoutedEventArgs e) + { + if (e.OriginalKey == VirtualKey.Shift || + e.OriginalKey == VirtualKey.LeftShift || + e.OriginalKey == VirtualKey.RightShift) { - _rootVisual = rootVisual; - _rootVisual.KeyDown += OnKeyDown; - _rootVisual.KeyUp += OnKeyUp; + _isShiftDown = false; } + } - private void OnKeyUp(object sender, KeyRoutedEventArgs e) + private void OnKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.OriginalKey == VirtualKey.Shift || + e.OriginalKey == VirtualKey.LeftShift || + e.OriginalKey == VirtualKey.RightShift) { - if (e.OriginalKey == VirtualKey.Shift || - e.OriginalKey == VirtualKey.LeftShift || - e.OriginalKey == VirtualKey.RightShift) - { - _isShiftDown = false; - } + _isShiftDown = true; } - private void OnKeyDown(object sender, KeyRoutedEventArgs e) + if (e.Handled) { - if (e.OriginalKey == VirtualKey.Shift || - e.OriginalKey == VirtualKey.LeftShift || - e.OriginalKey == VirtualKey.RightShift) - { - _isShiftDown = true; - } - - if (e.Handled) - { - return; - } + return; + } - if (e.OriginalKey == VirtualKey.Tab) - { - e.Handled = TryHandleTabFocus(_isShiftDown); - } + if (e.OriginalKey == VirtualKey.Tab) + { + e.Handled = TryHandleTabFocus(_isShiftDown); + } - if (e.OriginalKey == VirtualKey.Up || - e.OriginalKey == VirtualKey.Down || - e.OriginalKey == VirtualKey.Left || - e.OriginalKey == VirtualKey.Right) - { - e.Handled = TryHandleDirectionalFocus(e.OriginalKey); - } + if (e.OriginalKey == VirtualKey.Up || + e.OriginalKey == VirtualKey.Down || + e.OriginalKey == VirtualKey.Left || + e.OriginalKey == VirtualKey.Right) + { + e.Handled = TryHandleDirectionalFocus(e.OriginalKey); } + } - internal bool TryHandleTabFocus(bool isShiftDown) + internal bool TryHandleTabFocus(bool isShiftDown) + { + var direction = isShiftDown ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next; + var contentRoot = VisualTree.GetContentRootForElement(_rootElement); + if (contentRoot == null) { - var direction = isShiftDown ? FocusNavigationDirection.Previous : FocusNavigationDirection.Next; - var contentRoot = VisualTree.GetContentRootForElement(_rootVisual); - if (contentRoot == null) - { - return false; - } + return false; + } - contentRoot.InputManager.LastInputDeviceType = InputDeviceType.Keyboard; + contentRoot.InputManager.LastInputDeviceType = InputDeviceType.Keyboard; - var focusManager = VisualTree.GetFocusManagerForElement(_rootVisual); - var focusMovement = new FocusMovement(XYFocusOptions.Default, direction, null); - focusMovement.IsShiftPressed = _isShiftDown; - focusMovement.IsProcessingTab = true; - var result = focusManager?.FindAndSetNextFocus(focusMovement); - return result?.WasMoved == true; - } + var focusManager = VisualTree.GetFocusManagerForElement(_rootElement); + var focusMovement = new FocusMovement(XYFocusOptions.Default, direction, null); + focusMovement.IsShiftPressed = _isShiftDown; + focusMovement.IsProcessingTab = true; + var result = focusManager?.FindAndSetNextFocus(focusMovement); + return result?.WasMoved == true; + } - internal bool TryHandleDirectionalFocus(VirtualKey originalKey) + internal bool TryHandleDirectionalFocus(VirtualKey originalKey) + { + var contentRoot = VisualTree.GetContentRootForElement(_rootElement); + if (contentRoot == null) { - var contentRoot = VisualTree.GetContentRootForElement(_rootVisual); - if (contentRoot == null) - { - return false; - } - contentRoot.InputManager.LastInputDeviceType = InputDeviceType.Keyboard; - - var focusManager = VisualTree.GetFocusManagerForElement(_rootVisual); - var focusDirection = FocusSelection.GetNavigationDirectionForKeyboardArrow(originalKey); + return false; + } + contentRoot.InputManager.LastInputDeviceType = InputDeviceType.Keyboard; - if (focusManager == null || focusDirection == FocusNavigationDirection.None) - { - return false; - } + var focusManager = VisualTree.GetFocusManagerForElement(_rootElement); + var focusDirection = FocusSelection.GetNavigationDirectionForKeyboardArrow(originalKey); - var source = focusManager.FocusedElement; // Uno specific: This should actually bubble up with the event from the source element to the root visual. + if (focusManager == null || focusDirection == FocusNavigationDirection.None) + { + return false; + } - var directionalFocusEnabled = false; - var focusCandidateFound = false; - bool handled = false; - while (source != null && !focusCandidateFound) - { - var directionalFocusInfo = FocusSelection.TryDirectionalFocus(focusManager, focusDirection, source); - handled |= directionalFocusInfo.Handled; + var source = focusManager.FocusedElement; // Uno specific: This should actually bubble up with the event from the source element to the root visual. - focusCandidateFound |= directionalFocusInfo.FocusCandidateFound; - directionalFocusEnabled |= directionalFocusInfo.DirectionalFocusEnabled; + var directionalFocusEnabled = false; + var focusCandidateFound = false; + bool handled = false; + while (source != null && !focusCandidateFound) + { + var directionalFocusInfo = FocusSelection.TryDirectionalFocus(focusManager, focusDirection, source); + handled |= directionalFocusInfo.Handled; - if (!directionalFocusInfo.ShouldBubble) - { - break; - } + focusCandidateFound |= directionalFocusInfo.FocusCandidateFound; + directionalFocusEnabled |= directionalFocusInfo.DirectionalFocusEnabled; - if (!focusCandidateFound) - { - source = source.GetParent() as DependencyObject; - } + if (!directionalFocusInfo.ShouldBubble) + { + break; } - // Only raise NoFocusCandidateFound if XYDirectionalFocus was ever set to enabled and a - // focus candidate was never found. - if (directionalFocusEnabled && !focusCandidateFound) + if (!focusCandidateFound) { - focusManager.RaiseNoFocusCandidateFoundEvent(focusDirection); + source = source.GetParent() as DependencyObject; } + } - return handled; + // Only raise NoFocusCandidateFound if XYDirectionalFocus was ever set to enabled and a + // focus candidate was never found. + if (directionalFocusEnabled && !focusCandidateFound) + { + focusManager.RaiseNoFocusCandidateFoundEvent(focusDirection); } + + return handled; } } diff --git a/src/Uno.UI/UI/Xaml/Internal/VisualTree.cs b/src/Uno.UI/UI/Xaml/Internal/VisualTree.cs index 499414587b..8cb1852a9f 100644 --- a/src/Uno.UI/UI/Xaml/Internal/VisualTree.cs +++ b/src/Uno.UI/UI/Xaml/Internal/VisualTree.cs @@ -83,9 +83,9 @@ namespace Uno.UI.Xaml.Core RootVisual.AssociatedVisualTree = this; RootVisual.SetBackgroundColor(backgroundColor); RootElement = RootVisual; - - _focusInputHandler = new UnoFocusInputHandler(RootVisual); } + + _focusInputHandler = new UnoFocusInputHandler(RootElement); } internal UnoFocusInputHandler? UnoFocusInputHandler => _focusInputHandler; @@ -352,10 +352,8 @@ namespace Uno.UI.Xaml.Core /// /// Element. /// Root visual or null. - internal static RootVisual? GetRootForElement(DependencyObject? pObject) - { - return pObject?.GetContext().MainRootVisual; - } + internal static RootVisual? GetRootForElement(DependencyObject? pObject) => + VisualTree.GetForElement(pObject)?.RootVisual; /// /// Static helper function that encapsulates getting the FocusManager diff --git a/src/Uno.UI/UI/Xaml/UIElement.mux.cs b/src/Uno.UI/UI/Xaml/UIElement.mux.cs index 0535f16f0b..a8f012bd28 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.mux.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.mux.cs @@ -525,7 +525,7 @@ namespace Windows.UI.Xaml internal Rect GetGlobalBoundsLogical(bool ignoreClipping = false, bool useTargetInformation = false) { //TODO Uno specific: This implementation is significantly simplified from the actual WinUI implementation. - var rootVisual = VisualTree.GetRootForElement(this); + var rootVisual = VisualTree.GetRootForElement(this) ?? VisualTree.GetRootOrIslandForElement(this); if (rootVisual == null) { return Rect.Empty; -- GitLab