From 8b894cf57ac9f92aa5e9b800b12590f4e4446397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez=20Ruiz?= Date: Wed, 17 Mar 2021 14:55:24 +0100 Subject: [PATCH 1/3] Implement MaxLength property in EntryHandlers --- .../src/Android/Renderers/EntryRenderer.cs | 1 + .../Core/src/iOS/Renderers/EntryRenderer.cs | 1 + .../samples/Controls.Sample/Pages/MainPage.cs | 1 + src/Core/src/Core/ITextInput.cs | 5 ++ .../Handlers/Entry/EntryHandler.Android.cs | 5 ++ .../Handlers/Entry/EntryHandler.Standard.cs | 1 + src/Core/src/Handlers/Entry/EntryHandler.cs | 1 + .../src/Handlers/Entry/EntryHandler.iOS.cs | 5 ++ .../src/Platform/Android/EntryExtensions.cs | 56 +++++++++++++------ src/Core/src/Platform/iOS/EntryExtensions.cs | 8 +++ .../Handlers/Entry/EntryHandlerTests.cs | 25 ++++++++- src/Core/tests/DeviceTests/Stubs/EntryStub.cs | 6 +- 12 files changed, 94 insertions(+), 21 deletions(-) diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index c92869d31ee9..b60388f9cd54 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -365,6 +365,7 @@ void OnKeyboardBackPressed(object sender, EventArgs eventArgs) Control?.ClearFocus(); } + [PortHandler] void UpdateMaxLength() { var currentFilters = new List(EditText?.GetFilters() ?? new IInputFilter[0]); diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 12a87eb3bd7c..72d5226e7405 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -387,6 +387,7 @@ void UpdateCharacterSpacing() UpdateAttributedPlaceholder(placeHolder); } + [PortHandler] void UpdateMaxLength() { var currentControlText = Control.Text; diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 889bcbfac4c6..1b10999f70f0 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -94,6 +94,7 @@ void SetupMauiLayout() verticalStack.Add(new Entry { IsTextPredictionEnabled = false }); verticalStack.Add(new Entry { Placeholder = "This should be placeholder text" }); verticalStack.Add(new Entry { Text = "This should be read only property", IsReadOnly = true }); + verticalStack.Add(new Entry { MaxLength = 5, Placeholder = "MaxLength text" }); verticalStack.Add(new ProgressBar { Progress = 0.5 }); verticalStack.Add(new ProgressBar { Progress = 0.5, BackgroundColor = Color.LightCoral }); diff --git a/src/Core/src/Core/ITextInput.cs b/src/Core/src/Core/ITextInput.cs index 984688a51d62..4dbe0ccf8aec 100644 --- a/src/Core/src/Core/ITextInput.cs +++ b/src/Core/src/Core/ITextInput.cs @@ -14,5 +14,10 @@ public interface ITextInput : IText, IPlaceholder /// Gets a value indicating whether or not the view is read-only. /// bool IsReadOnly { get; } + + /// + /// Gets the maximum allowed length of input. + /// + int MaxLength { 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 011fc493d502..6cccc4ea8f4a 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -55,6 +55,11 @@ public static void MapIsTextPredictionEnabled(EntryHandler handler, IEntry entry handler.TypedNativeView?.UpdateIsTextPredictionEnabled(entry); } + public static void MapMaxLength(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateMaxLength(entry); + } + public static void MapPlaceholder(EntryHandler handler, IEntry entry) { handler.TypedNativeView?.UpdatePlaceholder(entry); diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs index 766dbe019db3..a780baeb11ea 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs @@ -10,6 +10,7 @@ public static void MapText(IViewHandler handler, IEntry entry) { } public static void MapTextColor(IViewHandler handler, IEntry entry) { } public static void MapIsPassword(IViewHandler handler, IEntry entry) { } public static void MapIsTextPredictionEnabled(IViewHandler handler, IEntry entry) { } + public static void MapMaxLength(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) { } diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs index a943c8dc1fe7..dd474db805ab 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -8,6 +8,7 @@ public partial class EntryHandler [nameof(IEntry.TextColor)] = MapTextColor, [nameof(IEntry.IsPassword)] = MapIsPassword, [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, + [nameof(IEntry.MaxLength)] = MapMaxLength, [nameof(IEntry.Placeholder)] = MapPlaceholder, [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, [nameof(IEntry.Font)] = MapFont diff --git a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs index c3248b3c4708..48bd8be1e688 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -62,6 +62,11 @@ public static void MapIsTextPredictionEnabled(EntryHandler handler, IEntry entry handler.TypedNativeView?.UpdateIsTextPredictionEnabled(entry); } + public static void MapMaxLength(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateMaxLength(entry); + } + public static void MapPlaceholder(EntryHandler handler, IEntry entry) { handler.TypedNativeView?.UpdatePlaceholder(entry); diff --git a/src/Core/src/Platform/Android/EntryExtensions.cs b/src/Core/src/Platform/Android/EntryExtensions.cs index 1abf2986a42d..4ac32ee79051 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -1,4 +1,5 @@ -using Android.Content.Res; +using System.Collections.Generic; +using Android.Content.Res; using Android.Text; using Android.Util; using AndroidX.AppCompat.Widget; @@ -45,27 +46,32 @@ public static void UpdateIsPassword(this AppCompatEditText editText, IEntry entr editText.SetInputType(entry); } - internal static void SetInputType(this AppCompatEditText editText, IEntry entry) + public static void UpdateIsTextPredictionEnabled(this AppCompatEditText editText, IEntry entry) { - editText.InputType = InputTypes.ClassText; - editText.InputType |= InputTypes.TextFlagMultiLine; + editText.SetInputType(entry); + } - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassText) == InputTypes.ClassText)) - editText.InputType |= InputTypes.TextVariationPassword; + public static void UpdateMaxLength(this AppCompatEditText editText, IEntry entry) + { + var currentFilters = new List(editText?.GetFilters() ?? new IInputFilter[0]); - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)) - editText.InputType |= InputTypes.NumberVariationPassword; + for (var i = 0; i < currentFilters.Count; i++) + { + if (currentFilters[i] is InputFilterLengthFilter) + { + currentFilters.RemoveAt(i); + break; + } + } - if (!entry.IsTextPredictionEnabled && ((editText.InputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) - editText.InputType |= InputTypes.TextFlagNoSuggestions; + currentFilters.Add(new InputFilterLengthFilter(entry.MaxLength)); - if (entry.IsReadOnly) - editText.InputType = InputTypes.Null; - } + editText?.SetFilters(currentFilters.ToArray()); - public static void UpdateIsTextPredictionEnabled(this AppCompatEditText editText, IEntry entry) - { - editText.SetInputType(entry); + var currentControlText = editText?.Text; + + if (editText != null && currentControlText != null && currentControlText.Length > entry.MaxLength) + editText.Text = currentControlText.Substring(0, entry.MaxLength); } public static void UpdatePlaceholder(this AppCompatEditText editText, IEntry entry) @@ -96,5 +102,23 @@ public static void UpdateFont(this AppCompatEditText editText, IEntry entry, IFo var sp = fontManager.GetScaledPixel(font); editText.SetTextSize(ComplexUnitType.Sp, sp); } + + internal static void SetInputType(this AppCompatEditText editText, IEntry entry) + { + editText.InputType = InputTypes.ClassText; + editText.InputType |= InputTypes.TextFlagMultiLine; + + if (entry.IsPassword && ((editText.InputType & InputTypes.ClassText) == InputTypes.ClassText)) + editText.InputType |= InputTypes.TextVariationPassword; + + if (entry.IsPassword && ((editText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)) + editText.InputType |= InputTypes.NumberVariationPassword; + + if (!entry.IsTextPredictionEnabled && ((editText.InputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) + editText.InputType |= InputTypes.TextFlagNoSuggestions; + + if (entry.IsReadOnly) + editText.InputType = InputTypes.Null; + } } } diff --git a/src/Core/src/Platform/iOS/EntryExtensions.cs b/src/Core/src/Platform/iOS/EntryExtensions.cs index 787a500a302f..1bf622f05a1e 100644 --- a/src/Core/src/Platform/iOS/EntryExtensions.cs +++ b/src/Core/src/Platform/iOS/EntryExtensions.cs @@ -46,6 +46,14 @@ public static void UpdateIsTextPredictionEnabled(this UITextField textField, IEn textField.AutocorrectionType = UITextAutocorrectionType.No; } + public static void UpdateMaxLength(this UITextField textField, IEntry entry) + { + var currentControlText = textField.Text; + + if (currentControlText?.Length > entry.MaxLength) + textField.Text = currentControlText.Substring(0, entry.MaxLength); + } + public static void UpdatePlaceholder(this UITextField textField, IEntry entry) { textField.Placeholder = entry.Placeholder; diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 32085afefd83..fcbb45b2391b 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Xunit; @@ -241,5 +240,25 @@ public async Task TextChangedEventsFireCorrectly(string initialText, string newT else Assert.Equal(0, eventFiredCount); } + + [Theory(DisplayName = "MaxLength Initializes Correctly")] + [InlineData(2)] + [InlineData(5)] + [InlineData(8)] + [InlineData(10)] + public async Task MaxLengthInitializesCorrectly(int maxLength) + { + string text = "Lorem ipsum dolor sit amet"; + + var entry = new EntryStub() + { + MaxLength = maxLength, + Text = text + }; + + var expected = text.Substring(0, maxLength); + + await ValidatePropertyInitValue(entry, () => entry.Text, GetNativeText, expected); + } } -} +} \ 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 32d8347a986d..df81e634b268 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -24,11 +24,13 @@ public string Text public bool IsReadOnly { get; set; } + public Font Font { get; set; } + + public int MaxLength { get; set; } = int.MaxValue; + public event EventHandler> TextChanged; void OnTextChanged(string oldValue, string newValue) => TextChanged?.Invoke(this, new StubPropertyChangedEventArgs(oldValue, newValue)); - - public Font Font { get; set; } } } \ No newline at end of file From c7315883bc729a63f70a73a73b7bd830a4934da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez=20Ruiz?= Date: Fri, 19 Mar 2021 13:03:35 +0100 Subject: [PATCH 2/3] Fix broken test --- .../Entry/EntryHandlerTests.Android.cs | 25 +++++++++++++++++- .../Handlers/Entry/EntryHandlerTests.cs | 20 -------------- .../Handlers/Entry/EntryHandlerTests.iOS.cs | 26 +++++++++++++++++++ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 6ee20546ce86..f628ebeccd0c 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -5,7 +5,7 @@ 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 +38,29 @@ public async Task FontFamilyInitializesCorrectly(string family) Assert.NotEqual(fontManager.DefaultTypeface, nativeEntry.Typeface); } + [Theory(DisplayName = "MaxLength Initializes Correctly")] + [InlineData(2)] + [InlineData(5)] + [InlineData(8)] + [InlineData(10)] + public async Task MaxLengthInitializesCorrectly(int maxLength) + { + string text = "Lorem ipsum dolor sit amet"; + + var entry = new EntryStub() + { + MaxLength = maxLength, + Text = text + }; + + var handler = await CreateHandlerAsync(entry); + var nativeEntry = GetNativeEntry(handler); + + var expected = text.Substring(0, maxLength); + + Assert.Equal(expected, nativeEntry.Text); + } + AppCompatEditText GetNativeEntry(EntryHandler entryHandler) => (AppCompatEditText)entryHandler.View; diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index fcbb45b2391b..6eb468a6d2b3 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -240,25 +240,5 @@ public async Task TextChangedEventsFireCorrectly(string initialText, string newT else Assert.Equal(0, eventFiredCount); } - - [Theory(DisplayName = "MaxLength Initializes Correctly")] - [InlineData(2)] - [InlineData(5)] - [InlineData(8)] - [InlineData(10)] - public async Task MaxLengthInitializesCorrectly(int maxLength) - { - string text = "Lorem ipsum dolor sit amet"; - - var entry = new EntryStub() - { - MaxLength = maxLength, - Text = text - }; - - var expected = text.Substring(0, maxLength); - - await ValidatePropertyInitValue(entry, () => entry.Text, GetNativeText, expected); - } } } \ 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 ba9462341f1c..eb8f68efba6d 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -34,6 +34,32 @@ public async Task FontFamilyInitializesCorrectly(string family) Assert.NotEqual(fontManager.DefaultFont.FamilyName, nativeFont.FamilyName); } + [Theory(DisplayName = "MaxLength Initializes Correctly")] + [InlineData(2)] + [InlineData(5)] + [InlineData(8)] + [InlineData(10)] + public async Task MaxLengthInitializesCorrectly(int maxLength) + { + string text = "Lorem ipsum dolor sit amet"; + + var entry = new EntryStub() + { + MaxLength = maxLength, + Text = text + }; + + await InvokeOnMainThreadAsync(async () => + { + var handler = await CreateHandlerAsync(entry); + var nativeEntry = GetNativeEntry(handler); + + var expected = text.Substring(0, maxLength); + + Assert.Equal(expected, nativeEntry.Text); + }); + } + UITextField GetNativeEntry(EntryHandler entryHandler) => (UITextField)entryHandler.View; From c613134cb966f69ceb8282aedb4cd578f14c75d3 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 22 Mar 2021 21:57:35 +0200 Subject: [PATCH 3/3] Fix iOS not updating related properties --- .../Core/src/iOS/Renderers/EntryRenderer.cs | 1 + .../src/Handlers/Entry/EntryHandler.iOS.cs | 45 +++++++++++++++-- .../Platform/Android/EditTextExtensions.cs | 11 ++--- .../Handlers/Editor/EditorHandlerTests.cs | 17 +++++++ .../Entry/EntryHandlerTests.Android.cs | 23 --------- .../Handlers/Entry/EntryHandlerTests.cs | 48 +++++++++++++++++++ .../Handlers/Entry/EntryHandlerTests.iOS.cs | 26 ---------- 7 files changed, 110 insertions(+), 61 deletions(-) diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 979128e31725..81f2059047fd 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -397,6 +397,7 @@ void UpdateMaxLength() Control.Text = currentControlText.Substring(0, Element.MaxLength); } + [PortHandler] bool ShouldChangeCharacters(UITextField textField, NSRange range, string replacementString) { var newLength = textField?.Text?.Length + replacementString?.Length - range.Length; diff --git a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs index 3a9bbfc551bf..362d224a86e9 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -1,4 +1,5 @@ using System; +using Foundation; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Platform.iOS; using UIKit; @@ -25,6 +26,7 @@ protected override void ConnectHandler(MauiTextField nativeView) nativeView.EditingChanged += OnEditingChanged; nativeView.EditingDidEnd += OnEditingEnded; nativeView.TextPropertySet += OnTextPropertySet; + nativeView.ShouldChangeCharacters += OnShouldChangeCharacters; } protected override void DisconnectHandler(MauiTextField nativeView) @@ -32,6 +34,7 @@ protected override void DisconnectHandler(MauiTextField nativeView) nativeView.EditingChanged -= OnEditingChanged; nativeView.EditingDidEnd -= OnEditingEnded; nativeView.TextPropertySet -= OnTextPropertySet; + nativeView.ShouldChangeCharacters -= OnShouldChangeCharacters; } protected override void SetupDefaults(MauiTextField nativeView) @@ -45,6 +48,9 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra public static void MapText(EntryHandler handler, IEntry entry) { handler.TypedNativeView?.UpdateText(entry); + + // Any text update requires that we update any attributed string formatting + MapFormatting(handler, entry); } public static void MapTextColor(EntryHandler handler, IEntry entry) @@ -87,6 +93,27 @@ public static void MapReturnType(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateReturnType(entry); } + public static void MapFont(EntryHandler handler, IEntry entry) + { + _ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class."); + + var fontManager = handler.Services.GetRequiredService(); + + handler.TypedNativeView?.UpdateFont(entry, fontManager); + } + + public static void MapFormatting(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateMaxLength(entry); + + // Update all of the attributed text formatting properties + handler.TypedNativeView?.UpdateCharacterSpacing(entry); + + // Setting any of those may have removed text alignment settings, + // so we need to make sure those are applied, too + handler.TypedNativeView?.UpdateHorizontalTextAlignment(entry); + } + void OnEditingChanged(object? sender, EventArgs e) => OnTextChanged(); void OnEditingEnded(object? sender, EventArgs e) => OnTextChanged(); @@ -106,13 +133,23 @@ void OnTextChanged() VirtualView.Text = nativeText; } - public static void MapFont(EntryHandler handler, IEntry entry) + bool OnShouldChangeCharacters(UITextField textField, NSRange range, string replacementString) { - _ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class."); + var currLength = textField?.Text?.Length ?? 0; - var fontManager = handler.Services.GetRequiredService(); + // fix a crash on undo + if (range.Length + range.Location > currLength) + return false; - handler.TypedNativeView?.UpdateFont(entry, fontManager); + if (VirtualView == null || TypedNativeView == null) + return false; + + var addLength = replacementString?.Length ?? 0; + var remLength = range.Length; + + var newLength = currLength + addLength - remLength; + + return newLength <= VirtualView.MaxLength; } } } \ No newline at end of file diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index b483f36cfd76..10a5fcdad32d 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -77,9 +77,9 @@ public static void UpdateIsTextPredictionEnabled(this AppCompatEditText editText public static void UpdateIsTextPredictionEnabled(this AppCompatEditText editText, IEditor editor) { if (editor.IsTextPredictionEnabled) - return; - - editText.InputType |= InputTypes.TextFlagNoSuggestions; + editText.InputType &= ~InputTypes.TextFlagNoSuggestions; + else + editText.InputType |= InputTypes.TextFlagNoSuggestions; } public static void UpdateMaxLength(this AppCompatEditText editText, IEntry entry) @@ -98,11 +98,6 @@ public static void UpdateMaxLength(this AppCompatEditText editText, IEntry entry currentFilters.Add(new InputFilterLengthFilter(entry.MaxLength)); editText?.SetFilters(currentFilters.ToArray()); - - var currentControlText = editText?.Text; - - if (editText != null && currentControlText != null && currentControlText.Length > entry.MaxLength) - editText.Text = currentControlText.Substring(0, entry.MaxLength); } public static void UpdatePlaceholder(this AppCompatEditText editText, IEntry entry) diff --git a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs index d4d4f7fc3e69..aa9efd175ddd 100644 --- a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs @@ -55,5 +55,22 @@ public async Task IsTextPredictionEnabledCorrectly(bool isEnabled) await ValidatePropertyInitValue(editor, () => editor.IsTextPredictionEnabled, GetNativeIsTextPredictionEnabled, isEnabled); } + + [Theory(DisplayName = "IsTextPredictionEnabled Updates Correctly")] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task IsTextPredictionEnabledUpdatesCorrectly(bool setValue, bool unsetValue) + { + var editor = new EditorStub(); + + await ValidatePropertyUpdatesValue( + editor, + nameof(IEditor.IsTextPredictionEnabled), + GetNativeIsTextPredictionEnabled, + setValue, + unsetValue); + } } } \ 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 7ebf842a2afc..03dc95d340e3 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -39,29 +39,6 @@ public async Task FontFamilyInitializesCorrectly(string family) Assert.NotEqual(fontManager.DefaultTypeface, nativeEntry.Typeface); } - [Theory(DisplayName = "MaxLength Initializes Correctly")] - [InlineData(2)] - [InlineData(5)] - [InlineData(8)] - [InlineData(10)] - public async Task MaxLengthInitializesCorrectly(int maxLength) - { - string text = "Lorem ipsum dolor sit amet"; - - var entry = new EntryStub() - { - MaxLength = maxLength, - Text = text - }; - - var handler = await CreateHandlerAsync(entry); - var nativeEntry = GetNativeEntry(handler); - - var expected = text.Substring(0, maxLength); - - Assert.Equal(expected, nativeEntry.Text); - } - [Fact(DisplayName = "ReturnType Initializes Correctly")] public async Task ReturnTypeInitializesCorrectly() { diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 06cc04bc1bee..0e6e4b5d9100 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -236,5 +236,53 @@ public async Task TextChangedEventsFireCorrectly(string initialText, string newT else Assert.Equal(0, eventFiredCount); } + + [Theory(DisplayName = "MaxLength Initializes Correctly")] + [InlineData(2)] + [InlineData(5)] + [InlineData(8)] + [InlineData(10)] + public async Task MaxLengthInitializesCorrectly(int maxLength) + { + const string text = "Lorem ipsum dolor sit amet"; + var expectedText = text.Substring(0, maxLength); + + var entry = new EntryStub() + { + MaxLength = maxLength, + Text = text + }; + + var nativeText = await GetValueAsync(entry, GetNativeText); + + Assert.Equal(expectedText, nativeText); + Assert.Equal(expectedText, entry.Text); + } + + [Theory(DisplayName = "MaxLength Clips Native Text Correctly")] + [InlineData(2)] + [InlineData(5)] + [InlineData(8)] + [InlineData(10)] + public async Task MaxLengthClipsNativeTextCorrectly(int maxLength) + { + const string text = "Lorem ipsum dolor sit amet"; + var expectedText = text.Substring(0, maxLength); + + var entry = new EntryStub() + { + MaxLength = maxLength, + }; + + var nativeText = await GetValueAsync(entry, handler => + { + entry.Text = text; + + return GetNativeText(handler); + }); + + Assert.Equal(expectedText, nativeText); + Assert.Equal(expectedText, entry.Text); + } } } \ 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 e686fd601a73..dd38f0be953a 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -34,32 +34,6 @@ public async Task FontFamilyInitializesCorrectly(string family) Assert.NotEqual(fontManager.DefaultFont.FamilyName, nativeFont.FamilyName); } - [Theory(DisplayName = "MaxLength Initializes Correctly")] - [InlineData(2)] - [InlineData(5)] - [InlineData(8)] - [InlineData(10)] - public async Task MaxLengthInitializesCorrectly(int maxLength) - { - string text = "Lorem ipsum dolor sit amet"; - - var entry = new EntryStub() - { - MaxLength = maxLength, - Text = text - }; - - await InvokeOnMainThreadAsync(async () => - { - var handler = await CreateHandlerAsync(entry); - var nativeEntry = GetNativeEntry(handler); - - var expected = text.Substring(0, maxLength); - - Assert.Equal(expected, nativeEntry.Text); - }); - } - [Fact(DisplayName = "Horizontal TextAlignment Initializes Correctly")] public async Task HorizontalTextAlignmentInitializesCorrectly() {