diff --git a/src/Compatibility/Core/src/Android/Extensions/EntryRendererExtensions.cs b/src/Compatibility/Core/src/Android/Extensions/EntryRendererExtensions.cs index b90f3377054a..ebb184154bab 100644 --- a/src/Compatibility/Core/src/Android/Extensions/EntryRendererExtensions.cs +++ b/src/Compatibility/Core/src/Android/Extensions/EntryRendererExtensions.cs @@ -4,7 +4,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { internal static class EntryRendererExtensions { - + [PortHandler] internal static ImeAction ToAndroidImeAction(this ReturnType returnType) { switch (returnType) diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index f272913392e0..f2acf4d9d985 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -397,6 +397,7 @@ void UpdateCharacterSpacing() } } + [PortHandler] void UpdateReturnType() { if (Control == null || Element == null) diff --git a/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs b/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs index e2d7ce489a45..607ee88e1371 100644 --- a/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs +++ b/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs @@ -111,6 +111,7 @@ internal static UISearchBarStyle ToNativeSearchBarStyle(this PlatformConfigurati } } + [PortHandler] internal static UIReturnKeyType ToUIReturnKeyType(this ReturnType returnType) { switch (returnType) diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index f928479f3430..af236d754bcf 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -402,6 +402,7 @@ bool ShouldChangeCharacters(UITextField textField, NSRange range, string replace return newLength <= Element?.MaxLength; } + [PortHandler] void UpdateReturnType() { if (Control == null || Element == null) diff --git a/src/Core/src/Core/IEntry.cs b/src/Core/src/Core/IEntry.cs index ee68a73a818a..e07f92c10101 100644 --- a/src/Core/src/Core/IEntry.cs +++ b/src/Core/src/Core/IEntry.cs @@ -14,5 +14,10 @@ public interface IEntry : IView, IText, ITextInput, ITextAlignment /// Gets a value that controls whether text prediction and automatic text correction is on or off. /// bool IsTextPredictionEnabled { get; } + + /// + /// Gets an enumeration value that controls the appearance of the return button. + /// + ReturnType ReturnType { get; } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs index bbbbe8a2c76d..f1c84c4f4e36 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -79,6 +79,11 @@ public static void MapIsReadOnly(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateIsReadOnly(entry); } + public static void MapReturnType(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateReturnType(entry); + } + void OnTextChanged(string? text) { if (VirtualView == null || TypedNativeView == null) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs index 1f5c439cb04e..4900bd45e574 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs @@ -14,5 +14,6 @@ public static void MapIsTextPredictionEnabled(IViewHandler handler, IEntry entry public static void MapPlaceholder(IViewHandler handler, IEntry entry) { } public static void MapIsReadOnly(IViewHandler handler, IEntry entry) { } public static void MapFont(IViewHandler handler, IEntry entry) { } + public static void MapReturnType(IViewHandler handler, IEntry entry) { } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs index b16011295ad5..aa1e935c99a6 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -11,7 +11,8 @@ public partial class EntryHandler [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, [nameof(IEntry.Placeholder)] = MapPlaceholder, [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, - [nameof(IEntry.Font)] = MapFont + [nameof(IEntry.Font)] = MapFont, + [nameof(IEntry.ReturnType)] = MapReturnType }; public EntryHandler() : base(EntryMapper) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs index 70e29a1d8766..aaa9e49221f6 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -77,6 +77,11 @@ public static void MapIsReadOnly(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateIsReadOnly(entry); } + public static void MapReturnType(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateReturnType(entry); + } + void OnEditingChanged(object? sender, EventArgs e) => OnTextChanged(); void OnEditingEnded(object? sender, EventArgs e) => OnTextChanged(); diff --git a/src/Core/src/Platform/Android/EntryExtensions.cs b/src/Core/src/Platform/Android/EntryExtensions.cs index 22de0444020a..fa0ed5a1cbb5 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -102,5 +102,10 @@ internal static void SetInputType(this AppCompatEditText editText, IEntry entry) if (entry.IsReadOnly) editText.InputType = InputTypes.Null; } + + public static void UpdateReturnType(this AppCompatEditText editText, IEntry entry) + { + editText.ImeOptions = entry.ReturnType.ToNative(); + } } } diff --git a/src/Core/src/Platform/Android/ImeActionExtensions.cs b/src/Core/src/Platform/Android/ImeActionExtensions.cs new file mode 100644 index 000000000000..1a208bd21b73 --- /dev/null +++ b/src/Core/src/Platform/Android/ImeActionExtensions.cs @@ -0,0 +1,29 @@ +using System; +using Android.Views.InputMethods; + +namespace Microsoft.Maui +{ + public static class ImeActionExtensions + { + public static ImeAction ToNative(this ReturnType returnType) + { + switch (returnType) + { + case ReturnType.Go: + return ImeAction.Go; + case ReturnType.Next: + return ImeAction.Next; + case ReturnType.Send: + return ImeAction.Send; + case ReturnType.Search: + return ImeAction.Search; + case ReturnType.Done: + return ImeAction.Done; + case ReturnType.Default: + return ImeAction.Done; + default: + throw new NotImplementedException($"ReturnType {returnType} not supported"); + } + } + } +} \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/EntryExtensions.cs b/src/Core/src/Platform/iOS/EntryExtensions.cs index 6ce7961a6000..861c08eaf577 100644 --- a/src/Core/src/Platform/iOS/EntryExtensions.cs +++ b/src/Core/src/Platform/iOS/EntryExtensions.cs @@ -69,5 +69,10 @@ public static void UpdateFont(this UITextField textField, IEntry entry, IFontMan var uiFont = fontManager.GetFont(entry.Font); textField.Font = uiFont; } + + public static void UpdateReturnType(this UITextField textField, IEntry entry) + { + textField.ReturnKeyType = entry.ReturnType.ToNative(); + } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/ReturnKeyTypeExtensions.cs b/src/Core/src/Platform/iOS/ReturnKeyTypeExtensions.cs new file mode 100644 index 000000000000..d6092fc99e7e --- /dev/null +++ b/src/Core/src/Platform/iOS/ReturnKeyTypeExtensions.cs @@ -0,0 +1,29 @@ +using System; +using UIKit; + +namespace Microsoft.Maui +{ + public static class ReturnKeyTypeExtensions + { + public static UIReturnKeyType ToNative(this ReturnType returnType) + { + switch (returnType) + { + case ReturnType.Go: + return UIReturnKeyType.Go; + case ReturnType.Next: + return UIReturnKeyType.Next; + case ReturnType.Send: + return UIReturnKeyType.Send; + case ReturnType.Search: + return UIReturnKeyType.Search; + case ReturnType.Done: + return UIReturnKeyType.Done; + case ReturnType.Default: + return UIReturnKeyType.Default; + default: + throw new NotImplementedException($"ReturnType {returnType} not supported"); + } + } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 887cd14e4b06..c89500f8cf86 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; using Android.Text; +using Android.Views.InputMethods; using AndroidX.AppCompat.Widget; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Xunit; -using AColor = global::Android.Graphics.Color; +using AColor = Android.Graphics.Color; namespace Microsoft.Maui.DeviceTests { @@ -38,6 +39,31 @@ public async Task FontFamilyInitializesCorrectly(string family) Assert.NotEqual(fontManager.DefaultTypeface, nativeEntry.Typeface); } + [Fact(DisplayName = "ReturnType Initializes Correctly")] + public async Task ReturnTypeInitializesCorrectly() + { + var xplatReturnType = ReturnType.Next; + var entry = new EntryStub() + { + Text = "Test", + ReturnType = xplatReturnType + }; + + ImeAction expectedValue = ImeAction.Next; + + var values = await GetValueAsync(entry, (handler) => + { + return new + { + ViewValue = entry.ReturnType, + NativeViewValue = GetNativeReturnType(handler) + }; + }); + + Assert.Equal(xplatReturnType, values.ViewValue); + Assert.Equal(expectedValue, values.NativeViewValue); + } + [Fact(DisplayName = "Horizontal TextAlignment Initializes Correctly")] public async Task HorizontalTextAlignmentInitializesCorrectly() { @@ -113,5 +139,8 @@ bool GetNativeIsItalic(EntryHandler entryHandler) => Android.Views.TextAlignment GetNativeTextAlignment(EntryHandler entryHandler) => GetNativeEntry(entryHandler).TextAlignment; + + ImeAction GetNativeReturnType(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).ImeOptions; } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs index e696e05e8867..9788e9329761 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -59,6 +59,32 @@ public async Task HorizontalTextAlignmentInitializesCorrectly() Assert.Equal(xplatHorizontalTextAlignment, values.ViewValue); values.NativeViewValue.AssertHasFlag(expectedValue); } + + [Fact(DisplayName = "ReturnType Initializes Correctly")] + public async Task ReturnTypeInitializesCorrectly() + { + var xplatReturnType = ReturnType.Next; + var entry = new EntryStub() + { + Text = "Test", + ReturnType = xplatReturnType + }; + + UIReturnKeyType expectedValue = UIReturnKeyType.Next; + + var values = await GetValueAsync(entry, (handler) => + { + return new + { + ViewValue = entry.ReturnType, + NativeViewValue = GetNativeReturnType(handler) + }; + }); + + Assert.Equal(xplatReturnType, values.ViewValue); + Assert.Equal(expectedValue, values.NativeViewValue); + } + UITextField GetNativeEntry(EntryHandler entryHandler) => (UITextField)entryHandler.View; @@ -95,5 +121,8 @@ bool GetNativeIsItalic(EntryHandler entryHandler) => UITextAlignment GetNativeTextAlignment(EntryHandler entryHandler) => GetNativeEntry(entryHandler).TextAlignment; + + UIReturnKeyType GetNativeReturnType(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).ReturnKeyType; } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs index f2a4e27df32c..026fbec2f611 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -28,6 +28,8 @@ public string Text public TextAlignment HorizontalTextAlignment { get; set; } + public ReturnType ReturnType { get; set; } + public event EventHandler> TextChanged; void OnTextChanged(string oldValue, string newValue) =>