From 2fffd373b797cd4643b5458325a9e44094aae26e Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 Feb 2023 18:53:16 -0600 Subject: [PATCH 01/16] - updates --- src/Controls/src/Core/Entry/Entry.Android.cs | 13 +++++++++ src/Controls/src/Core/Entry/Entry.Mapper.cs | 8 +++++ .../VisualElement/VisualElement.cs | 29 +++++++++++++++++++ src/Controls/src/Core/VisualElement.cs | 25 ++-------------- .../src/Platform/Android/KeyboardManager.cs | 5 +++- 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs index 65db5337593a..c8e469bd546e 100644 --- a/src/Controls/src/Core/Entry/Entry.Android.cs +++ b/src/Controls/src/Core/Entry/Entry.Android.cs @@ -30,5 +30,18 @@ public static void MapText(IEntryHandler handler, Entry entry) { Platform.EditTextExtensions.UpdateText(handler.PlatformView, entry); } + + static void MapFocus(IViewHandler handler, IView view, object args) + { + if (view is not VisualElement ve) + return; + + if (ve.IsFocused) + { + KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + } + + EntryHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); + } } } diff --git a/src/Controls/src/Core/Entry/Entry.Mapper.cs b/src/Controls/src/Core/Entry/Entry.Mapper.cs index 21b067f61bbe..5f5328ff8fdb 100644 --- a/src/Controls/src/Core/Entry/Entry.Mapper.cs +++ b/src/Controls/src/Core/Entry/Entry.Mapper.cs @@ -18,10 +18,18 @@ public partial class Entry [nameof(TextTransform)] = MapText, }; + static CommandMapper ControlsCommandMapper = new(EntryHandler.CommandMapper) + { +#if ANDROID + [nameof(IEntry.Focus)] = MapFocus +#endif + }; + internal static new void RemapForControls() { // Adjust the mappings to preserve Controls.Entry legacy behaviors EntryHandler.Mapper = ControlsEntryMapper; + EntryHandler.CommandMapper = ControlsCommandMapper; } } } diff --git a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs index ba5056f81ce1..af8a5d41d980 100644 --- a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs @@ -24,9 +24,15 @@ public partial class VisualElement [nameof(IViewHandler.ContainerView)] = MapContainerView, }; + static CommandMapper ControlsViewCommandMapper = new(ViewHandler.ViewCommandMapper) + { + [nameof(IView.Focus)] = MapFocus, + }; + internal static void RemapForControls() { ViewHandler.ViewMapper = ControlsVisualElementMapper; + ViewHandler.ViewCommandMapper = ControlsViewCommandMapper; } public static void MapBackgroundColor(IViewHandler handler, IView view) @@ -63,5 +69,28 @@ static void MapContainerView(IViewHandler arg1, IView arg2) ve._platformContainerViewChanged?.Invoke(arg2, EventArgs.Empty); } } + + static void MapFocus(IViewHandler handler, IView view, object args) + { + if (args is not FocusRequest fr) + return; + + if (view is not VisualElement ve) + return; + + if (ve.IsFocused) + return; + + if (ve.FocusChangeRequested == null) + { + FocusRequest focusRequest = new FocusRequest(false); + ViewHandler.ViewCommandMapper?.Invoke(handler, view, nameof(IView.Focus), fr); + return; + } + + var arg = new FocusRequestArgs { Focus = true }; + ve.FocusChangeRequested(ve, arg); + fr.IsFocused = arg.Result; + } } } diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index 132e0750dfdc..d86048aa174c 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -871,28 +871,9 @@ public ResourceDictionary Resources /// public bool Focus() { - if (IsFocused) - { -#if ANDROID - // TODO: Refactor using mappers for .NET 8 - if (this is ITextInput && Handler is IPlatformViewHandler platformViewHandler) - KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); -#endif - return true; - } - - if (FocusChangeRequested == null) - { - FocusRequest focusRequest = new FocusRequest(false); - - Handler?.Invoke(nameof(IView.Focus), focusRequest); - - return focusRequest.IsFocused; - } - - var arg = new FocusRequestArgs { Focus = true }; - FocusChangeRequested(this, arg); - return arg.Result; + FocusRequest focusRequest = new FocusRequest(false); + Handler?.Invoke(nameof(IView.Focus), focusRequest); + return focusRequest.IsFocused; } public event EventHandler Focused; diff --git a/src/Core/src/Platform/Android/KeyboardManager.cs b/src/Core/src/Platform/Android/KeyboardManager.cs index f5d3c02c8b18..fe081af71cc3 100644 --- a/src/Core/src/Platform/Android/KeyboardManager.cs +++ b/src/Core/src/Platform/Android/KeyboardManager.cs @@ -4,6 +4,7 @@ using Android.Widget; using AndroidX.AppCompat.App; using AndroidX.Core.View; +using Microsoft.Maui.ApplicationModel; using AView = Android.Views.View; using SearchView = AndroidX.AppCompat.Widget.SearchView; @@ -38,7 +39,9 @@ internal static bool ShowKeyboard(this TextView inputView) if (inputView?.Context is null) throw new ArgumentNullException(nameof(inputView) + " must be set before the keyboard can be shown."); - using (var inputMethodManager = inputView.Context.GetSystemService(Context.InputMethodService) as InputMethodManager) + if (inputView.IsFocused) + Show(); + else { // The zero value for the second parameter comes from // https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#showSoftInput(android.view.View,%20int) From 32fc301e423a0697fb3d88add8e564107c006b0f Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Fri, 10 Mar 2023 11:01:36 -0500 Subject: [PATCH 02/16] Add missing using --- src/Core/src/Platform/Android/KeyboardManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/src/Platform/Android/KeyboardManager.cs b/src/Core/src/Platform/Android/KeyboardManager.cs index fe081af71cc3..a41d5ac31042 100644 --- a/src/Core/src/Platform/Android/KeyboardManager.cs +++ b/src/Core/src/Platform/Android/KeyboardManager.cs @@ -1,5 +1,6 @@ using System; using Android.Content; +using Android.OS; using Android.Views.InputMethods; using Android.Widget; using AndroidX.AppCompat.App; From 582eebff8aa9934eb7838f2f005f235d88b40458 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Fri, 10 Mar 2023 11:02:37 -0500 Subject: [PATCH 03/16] Add mappers for Editor and SearchBar too --- .../src/Core/HandlerImpl/Editor/Editor.Android.cs | 13 +++++++++++++ src/Controls/src/Core/HandlerImpl/Editor/Editor.cs | 8 ++++++++ .../Core/HandlerImpl/SearchBar/SearchBar.Android.cs | 13 +++++++++++++ .../src/Core/HandlerImpl/SearchBar/SearchBar.cs | 8 ++++++++ 4 files changed, 42 insertions(+) diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs index c95c31b81cf8..0ca7b362c825 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs @@ -18,5 +18,18 @@ public static void MapText(IEditorHandler handler, Editor editor) { Platform.EditTextExtensions.UpdateText(handler.PlatformView, editor); } + + static void MapFocus(IViewHandler handler, IView view, object args) + { + if (view is not VisualElement ve) + return; + + if (ve.IsFocused) + { + KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + } + + EditorHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); + } } } diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.cs index dca8ec4444ce..221cc31be702 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.cs @@ -13,10 +13,18 @@ public partial class Editor [nameof(TextTransform)] = MapText, }; + static CommandMapper ControlsCommandMapper = new(EditorHandler.CommandMapper) + { +#if ANDROID + [nameof(IEditor.Focus)] = MapFocus +#endif + }; + internal static new void RemapForControls() { // Adjust the mappings to preserve Controls.Editor legacy behaviors EditorHandler.Mapper = ControlsEditorMapper; + EditorHandler.CommandMapper = ControlsCommandMapper; } } } \ No newline at end of file diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs index ca77bc211caf..90dd5d2cf698 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs @@ -10,5 +10,18 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar) { Platform.SearchViewExtensions.UpdateText(handler.PlatformView, searchBar); } + + static void MapFocus(IViewHandler handler, IView view, object args) + { + if (view is not VisualElement ve) + return; + + if (ve.IsFocused) + { + KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + } + + SearchBarHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); + } } } diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.cs index e902fb772987..56d6ba28a0eb 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.cs @@ -15,10 +15,18 @@ public partial class SearchBar [nameof(TextTransform)] = MapText, }; + static CommandMapper ControlsCommandMapper = new(SearchBarHandler.CommandMapper) + { +#if ANDROID + [nameof(ISearchBar.Focus)] = MapFocus +#endif + }; + internal static new void RemapForControls() { // Adjust the mappings to preserve Controls.SearchBar legacy behaviors SearchBarHandler.Mapper = ControlsSearchBarMapper; + SearchBarHandler.CommandMapper = ControlsCommandMapper; } } } From e2ab94bda5b484a3211ea6aea0828bf4de9e316c Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Fri, 10 Mar 2023 17:26:47 -0500 Subject: [PATCH 04/16] Tweak based on feedback --- src/Controls/src/Core/Entry/Entry.Android.cs | 4 ++-- src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs | 4 ++-- .../src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs | 4 ++-- .../src/Core/HandlerImpl/VisualElement/VisualElement.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs index c8e469bd546e..fd1f5e3e5354 100644 --- a/src/Controls/src/Core/Entry/Entry.Android.cs +++ b/src/Controls/src/Core/Entry/Entry.Android.cs @@ -36,9 +36,9 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (view is not VisualElement ve) return; - if (ve.IsFocused) + if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { - KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } EntryHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs index 0ca7b362c825..a3b740187407 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs @@ -24,9 +24,9 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (view is not VisualElement ve) return; - if (ve.IsFocused) + if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { - KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } EditorHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs index 90dd5d2cf698..c4c2a28e1aef 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs @@ -16,9 +16,9 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (view is not VisualElement ve) return; - if (ve.IsFocused) + if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { - KeyboardManager.ShowKeyboard((handler as IPlatformViewHandler).PlatformView); + KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } SearchBarHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); diff --git a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs index af8a5d41d980..f5e47231ea75 100644 --- a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs @@ -81,7 +81,7 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (ve.IsFocused) return; - if (ve.FocusChangeRequested == null) + if (ve.FocusChangeRequested is null) { FocusRequest focusRequest = new FocusRequest(false); ViewHandler.ViewCommandMapper?.Invoke(handler, view, nameof(IView.Focus), fr); From c2c8a9e1de7117a4fa96f24af11bf66cf54a7014 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Fri, 17 Mar 2023 15:22:34 -0400 Subject: [PATCH 05/16] Update KeyboardManager with main --- src/Core/src/Platform/Android/KeyboardManager.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Core/src/Platform/Android/KeyboardManager.cs b/src/Core/src/Platform/Android/KeyboardManager.cs index a41d5ac31042..f5d3c02c8b18 100644 --- a/src/Core/src/Platform/Android/KeyboardManager.cs +++ b/src/Core/src/Platform/Android/KeyboardManager.cs @@ -1,11 +1,9 @@ using System; using Android.Content; -using Android.OS; using Android.Views.InputMethods; using Android.Widget; using AndroidX.AppCompat.App; using AndroidX.Core.View; -using Microsoft.Maui.ApplicationModel; using AView = Android.Views.View; using SearchView = AndroidX.AppCompat.Widget.SearchView; @@ -40,9 +38,7 @@ internal static bool ShowKeyboard(this TextView inputView) if (inputView?.Context is null) throw new ArgumentNullException(nameof(inputView) + " must be set before the keyboard can be shown."); - if (inputView.IsFocused) - Show(); - else + using (var inputMethodManager = inputView.Context.GetSystemService(Context.InputMethodService) as InputMethodManager) { // The zero value for the second parameter comes from // https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#showSoftInput(android.view.View,%20int) From b8c7c306f06c1d9838e163d48dc29b7c4453b6aa Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Tue, 21 Mar 2023 15:11:52 -0400 Subject: [PATCH 06/16] Remove unnecessary check and make tests happy --- src/Controls/src/Core/Entry/Entry.Android.cs | 5 +---- src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs | 5 +---- .../src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs index fd1f5e3e5354..7e6f27c287c4 100644 --- a/src/Controls/src/Core/Entry/Entry.Android.cs +++ b/src/Controls/src/Core/Entry/Entry.Android.cs @@ -33,10 +33,7 @@ public static void MapText(IEntryHandler handler, Entry entry) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is not VisualElement ve) - return; - - if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs index a3b740187407..82c0719bc800 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs @@ -21,10 +21,7 @@ public static void MapText(IEditorHandler handler, Editor editor) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is not VisualElement ve) - return; - - if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs index c4c2a28e1aef..d557a4d3713f 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs @@ -13,10 +13,7 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is not VisualElement ve) - return; - - if (ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } From 926a343d702f02ec478fa9569a58b4041f996845 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Tue, 21 Mar 2023 15:33:29 -0400 Subject: [PATCH 07/16] Add check back and combine with other conditions --- src/Controls/src/Core/Entry/Entry.Android.cs | 2 +- src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs | 2 +- .../src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs index 7e6f27c287c4..313b1e3c1222 100644 --- a/src/Controls/src/Core/Entry/Entry.Android.cs +++ b/src/Controls/src/Core/Entry/Entry.Android.cs @@ -33,7 +33,7 @@ public static void MapText(IEntryHandler handler, Entry entry) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs index 82c0719bc800..ccfdf3ebd100 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs @@ -21,7 +21,7 @@ public static void MapText(IEditorHandler handler, Editor editor) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs index d557a4d3713f..1866c5ab465d 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs @@ -13,7 +13,7 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view.IsFocused && handler is IPlatformViewHandler platformViewHandler) + if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) { KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); } From c73ccdc4db7c78a209acd274d9c7edb7bffda0d7 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Wed, 22 Mar 2023 13:55:07 -0400 Subject: [PATCH 08/16] Tweak failing test --- .../Handlers/SearchBar/SearchBarHandlerTests.iOS.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs index 724d8a2dc1d6..6c0a408ecbf7 100644 --- a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs @@ -191,11 +191,11 @@ await InvokeOnMainThreadAsync(async () => var searchBarHandler = CreateHandler(searchBar); await searchBarHandler.PlatformView.AttachAndRun(async () => { - searchBar.Handler.Invoke(nameof(IView.Focus), new FocusRequest(false)); + searchBarHandler.PlatformView.Focus(new FocusRequest(false)); await searchBar.WaitForFocused(); Assert.True(searchBar.IsFocused); - searchBar.Handler.Invoke(nameof(IView.Unfocus), new FocusRequest(false)); + searchBarHandler.PlatformView.Unfocus(searchBar); await searchBar.WaitForUnFocused(); Assert.False(searchBar.IsFocused); }); From cb71a6ecf1bf965765fa9cafbfad251b0abc4ae1 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Thu, 23 Mar 2023 13:57:16 -0400 Subject: [PATCH 09/16] Increase delay on Send KeyboardReturnType test --- src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs index 6c4eebb482d4..b3b306bb5a5b 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Android.cs @@ -45,7 +45,7 @@ public static async Task SendKeyboardReturnType(this AView view, ReturnType retu .PerformEditorAction(returnType.ToPlatform()); // Let the action propagate - await Task.Delay(10); + await Task.Delay(100); } public static async Task WaitForFocused(this AView view, int timeout = 1000, string message = "") From 9b739b0e83376b5ed45e8a2ce2ffcd5e96182e78 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Tue, 28 Mar 2023 16:56:59 -0400 Subject: [PATCH 10/16] Substitute invoke with mapper call --- .../src/Core/HandlerImpl/VisualElement/VisualElement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs index f5e47231ea75..626b3def9f07 100644 --- a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs @@ -84,7 +84,7 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (ve.FocusChangeRequested is null) { FocusRequest focusRequest = new FocusRequest(false); - ViewHandler.ViewCommandMapper?.Invoke(handler, view, nameof(IView.Focus), fr); + ViewHandler.MapFocus(handler, view, focusRequest); return; } From b937e04afe6d021ae01bd4b839593aaab0456826 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Wed, 29 Mar 2023 13:05:59 -0400 Subject: [PATCH 11/16] Tweak VisualElement MapFocus code --- .../src/Core/HandlerImpl/VisualElement/VisualElement.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs index 626b3def9f07..b21df5812826 100644 --- a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs @@ -79,12 +79,18 @@ static void MapFocus(IViewHandler handler, IView view, object args) return; if (ve.IsFocused) + { + fr.IsFocused = true; return; + } if (ve.FocusChangeRequested is null) { FocusRequest focusRequest = new FocusRequest(false); - ViewHandler.MapFocus(handler, view, focusRequest); + + if (handler is not null) + ViewHandler.MapFocus(handler, view, focusRequest); + return; } From 25b09da66a46e58c7d000e0bcd9b09c45e0bf12f Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 29 Mar 2023 12:33:08 -0500 Subject: [PATCH 12/16] Fix --- .../VisualElement/VisualElement.cs | 23 +-------- src/Controls/src/Core/ViewExtensions.cs | 50 +++++++++++++++++++ src/Controls/src/Core/VisualElement.cs | 6 ++- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs index b21df5812826..b8533ca4c86c 100644 --- a/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs +++ b/src/Controls/src/Core/HandlerImpl/VisualElement/VisualElement.cs @@ -75,28 +75,7 @@ static void MapFocus(IViewHandler handler, IView view, object args) if (args is not FocusRequest fr) return; - if (view is not VisualElement ve) - return; - - if (ve.IsFocused) - { - fr.IsFocused = true; - return; - } - - if (ve.FocusChangeRequested is null) - { - FocusRequest focusRequest = new FocusRequest(false); - - if (handler is not null) - ViewHandler.MapFocus(handler, view, focusRequest); - - return; - } - - var arg = new FocusRequestArgs { Focus = true }; - ve.FocusChangeRequested(ve, arg); - fr.IsFocused = arg.Result; + view.MapFocus(handler, fr); } } } diff --git a/src/Controls/src/Core/ViewExtensions.cs b/src/Controls/src/Core/ViewExtensions.cs index 7526813ab2ea..542a90999844 100644 --- a/src/Controls/src/Core/ViewExtensions.cs +++ b/src/Controls/src/Core/ViewExtensions.cs @@ -443,5 +443,55 @@ internal static bool TrySetValue(this Element element, string text) return false; } + + static internal bool Focus( + this VisualElement view, + FocusRequest focusRequest) + { + var handler = view.Handler; + + if (handler is not null) + { + handler.Invoke(nameof(IView.Focus), focusRequest); + } + else + { + MapFocus(view, handler, focusRequest); + } + + return focusRequest.IsFocused; + } + + // If the handler isn't attached we still need some focus code to execute + static internal void MapFocus( + this IView view, + IViewHandler? handler, + FocusRequest args) + { + if (args is not FocusRequest fr) + return; + + if (view is not VisualElement ve) + return; + + if (ve.IsFocused) + { + fr.IsFocused = true; + return; + } + + if (!ve.HasFocusChangeRequestedEvent) + { + FocusRequest focusRequest = new FocusRequest(false); + if (handler is not null) + ViewHandler.MapFocus(handler, view, focusRequest); + + return; + } + + var arg = new VisualElement.FocusRequestArgs { Focus = true }; + fr.IsFocused = arg.Result; + ve.InvokeFocusChangeRequested(arg); + } } } diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index d86048aa174c..b4ab3f1246f9 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -872,8 +872,7 @@ public ResourceDictionary Resources public bool Focus() { FocusRequest focusRequest = new FocusRequest(false); - Handler?.Invoke(nameof(IView.Focus), focusRequest); - return focusRequest.IsFocused; + return this.Focus(focusRequest); } public event EventHandler Focused; @@ -1038,6 +1037,9 @@ internal void ComputeConstrainsForChildren() [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler FocusChangeRequested; + internal void InvokeFocusChangeRequested(FocusRequestArgs args) => + FocusChangeRequested?.Invoke(this, args); + internal bool HasFocusChangeRequestedEvent => FocusChangeRequested is not null; /// [EditorBrowsable(EditorBrowsableState.Never)] From ed1366782a39f996d00f00a9e0c964ba64a76999 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Thu, 30 Mar 2023 12:44:48 -0400 Subject: [PATCH 13/16] Clean code and reorder InvokeFocusChangeRequested to fix test --- src/Controls/src/Core/ViewExtensions.cs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Controls/src/Core/ViewExtensions.cs b/src/Controls/src/Core/ViewExtensions.cs index 542a90999844..4fbb2891a824 100644 --- a/src/Controls/src/Core/ViewExtensions.cs +++ b/src/Controls/src/Core/ViewExtensions.cs @@ -444,29 +444,20 @@ internal static bool TrySetValue(this Element element, string text) return false; } - static internal bool Focus( - this VisualElement view, - FocusRequest focusRequest) + static internal bool Focus(this VisualElement view, FocusRequest focusRequest) { var handler = view.Handler; if (handler is not null) - { handler.Invoke(nameof(IView.Focus), focusRequest); - } else - { MapFocus(view, handler, focusRequest); - } return focusRequest.IsFocused; } // If the handler isn't attached we still need some focus code to execute - static internal void MapFocus( - this IView view, - IViewHandler? handler, - FocusRequest args) + static internal void MapFocus(this IView view, IViewHandler? handler, FocusRequest args) { if (args is not FocusRequest fr) return; @@ -490,8 +481,8 @@ static internal void MapFocus( } var arg = new VisualElement.FocusRequestArgs { Focus = true }; - fr.IsFocused = arg.Result; ve.InvokeFocusChangeRequested(arg); + fr.IsFocused = arg.Result; } } } From 3b2061b5c5e041906a507733de32d088fff39801 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Thu, 30 Mar 2023 13:06:36 -0400 Subject: [PATCH 14/16] Add TODO comment --- src/Controls/src/Core/VisualElement.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controls/src/Core/VisualElement.cs b/src/Controls/src/Core/VisualElement.cs index b4ab3f1246f9..caea729e4b52 100644 --- a/src/Controls/src/Core/VisualElement.cs +++ b/src/Controls/src/Core/VisualElement.cs @@ -1035,6 +1035,7 @@ internal void ComputeConstrainsForChildren() internal virtual void ComputeConstraintForView(View view) => view.ComputedConstraint = LayoutConstraint.None; + // TODO: Obsolete in favor of MapFocus https://github.com/dotnet/maui/issues/14299 [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler FocusChangeRequested; internal void InvokeFocusChangeRequested(FocusRequestArgs args) => From 5dda19d90f7508613207270f13828fe597fbf997 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Fri, 31 Mar 2023 15:00:54 -0400 Subject: [PATCH 15/16] Extract show keyboard logic to extension method --- src/Controls/src/Core/Entry/Entry.Android.cs | 7 ++----- .../src/Core/HandlerImpl/Editor/Editor.Android.cs | 8 +++----- .../HandlerImpl/SearchBar/SearchBar.Android.cs | 8 +++----- .../Android/Extensions/InputViewExtensions.cs | 15 +++++++++++++++ 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 src/Controls/src/Core/Platform/Android/Extensions/InputViewExtensions.cs diff --git a/src/Controls/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs index 313b1e3c1222..7c670e609020 100644 --- a/src/Controls/src/Core/Entry/Entry.Android.cs +++ b/src/Controls/src/Core/Entry/Entry.Android.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.Maui.Controls.Platform; namespace Microsoft.Maui.Controls { @@ -33,11 +34,7 @@ public static void MapText(IEntryHandler handler, Entry entry) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) - { - KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); - } - + handler.ShowKeyboardIfFocused(view); EntryHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); } } diff --git a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs index ccfdf3ebd100..aee3ae678a95 100644 --- a/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/Editor/Editor.Android.cs @@ -1,4 +1,6 @@ #nullable disable +using Microsoft.Maui.Controls.Platform; + namespace Microsoft.Maui.Controls { public partial class Editor @@ -21,11 +23,7 @@ public static void MapText(IEditorHandler handler, Editor editor) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) - { - KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); - } - + handler.ShowKeyboardIfFocused(view); EditorHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); } } diff --git a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs index 1866c5ab465d..e9b47703f741 100644 --- a/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs +++ b/src/Controls/src/Core/HandlerImpl/SearchBar/SearchBar.Android.cs @@ -1,4 +1,6 @@ #nullable disable +using Microsoft.Maui.Controls.Platform; + namespace Microsoft.Maui.Controls { public partial class SearchBar @@ -13,11 +15,7 @@ public static void MapText(ISearchBarHandler handler, SearchBar searchBar) static void MapFocus(IViewHandler handler, IView view, object args) { - if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) - { - KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); - } - + handler.ShowKeyboardIfFocused(view); SearchBarHandler.CommandMapper.Chained?.Invoke(handler, view, nameof(IView.Focus), args); } } diff --git a/src/Controls/src/Core/Platform/Android/Extensions/InputViewExtensions.cs b/src/Controls/src/Core/Platform/Android/Extensions/InputViewExtensions.cs new file mode 100644 index 000000000000..1a90ce010b86 --- /dev/null +++ b/src/Controls/src/Core/Platform/Android/Extensions/InputViewExtensions.cs @@ -0,0 +1,15 @@ +#nullable disable + +namespace Microsoft.Maui.Controls.Platform +{ + internal static class InputViewExtensions + { + internal static void ShowKeyboardIfFocused(this IViewHandler handler, IView view) + { + if (view is VisualElement ve && ve.IsFocused && handler is IPlatformViewHandler platformViewHandler) + { + KeyboardManager.ShowKeyboard(platformViewHandler.PlatformView); + } + } + } +} \ No newline at end of file From 84d2760a721df0ebcfc087cfb5a7aac3cfa3d595 Mon Sep 17 00:00:00 2001 From: Rachel Kang Date: Mon, 3 Apr 2023 12:10:48 -0400 Subject: [PATCH 16/16] Restore test logic and tweak code to make pass --- src/Controls/src/Core/ViewExtensions.cs | 5 +++++ .../Handlers/SearchBar/SearchBarHandlerTests.iOS.cs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Controls/src/Core/ViewExtensions.cs b/src/Controls/src/Core/ViewExtensions.cs index 4fbb2891a824..6fc7bb2bfa77 100644 --- a/src/Controls/src/Core/ViewExtensions.cs +++ b/src/Controls/src/Core/ViewExtensions.cs @@ -463,7 +463,12 @@ static internal void MapFocus(this IView view, IViewHandler? handler, FocusReque return; if (view is not VisualElement ve) + { + FocusRequest focusRequest = new FocusRequest(false); + if (handler is not null) + ViewHandler.MapFocus(handler, view, focusRequest); return; + } if (ve.IsFocused) { diff --git a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs index 6c0a408ecbf7..724d8a2dc1d6 100644 --- a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs @@ -191,11 +191,11 @@ await InvokeOnMainThreadAsync(async () => var searchBarHandler = CreateHandler(searchBar); await searchBarHandler.PlatformView.AttachAndRun(async () => { - searchBarHandler.PlatformView.Focus(new FocusRequest(false)); + searchBar.Handler.Invoke(nameof(IView.Focus), new FocusRequest(false)); await searchBar.WaitForFocused(); Assert.True(searchBar.IsFocused); - searchBarHandler.PlatformView.Unfocus(searchBar); + searchBar.Handler.Invoke(nameof(IView.Unfocus), new FocusRequest(false)); await searchBar.WaitForUnFocused(); Assert.False(searchBar.IsFocused); });