diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index 78deb8331b30..adca1ceb9c23 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -517,6 +517,7 @@ void SetSelectionLengthFromRenderer(int selectionLength) } } + [PortHandler] protected virtual void UpdateIsReadOnly() { bool isReadOnly = !Element.IsReadOnly; diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 07dbe9adb382..0dc919d790a5 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -549,6 +549,7 @@ void SetSelectionLengthFromRenderer(int selectionLength) } } + [PortHandler] void UpdateIsReadOnly() { Control.UserInteractionEnabled = !Element.IsReadOnly; diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 95c673e628d9..1c4a8803aa01 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -59,6 +59,7 @@ void SetupMauiLayout() verticalStack.Add(new Entry { Text = "Entry", TextColor = Color.DarkRed }); verticalStack.Add(new Entry { IsPassword = true, TextColor = Color.Black }); verticalStack.Add(new Entry { IsTextPredictionEnabled = false }); + verticalStack.Add(new Entry { Text = "This should be read only property", IsReadOnly = true }); verticalStack.Add(new Slider()); diff --git a/src/Core/src/Core/ITextInput.cs b/src/Core/src/Core/ITextInput.cs index 3ae21492bcce..1dc298efe9d0 100644 --- a/src/Core/src/Core/ITextInput.cs +++ b/src/Core/src/Core/ITextInput.cs @@ -9,5 +9,7 @@ public interface ITextInput : IText /// Gets the maximum allowed length of input. /// new string Text { get; set; } + + bool IsReadOnly { 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 6f47472de1f0..1eeebea5ceba 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -53,6 +53,11 @@ public static void MapIsTextPredictionEnabled(EntryHandler handler, IEntry entry handler.TypedNativeView?.UpdateIsTextPredictionEnabled(entry); } + public static void MapIsReadOnly(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateIsReadOnly(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 d3bec8b6a1fc..9ea913b4f0f6 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs @@ -10,5 +10,6 @@ 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 MapIsReadOnly(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 0e5c523c98c0..447b61f6f39d 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -7,7 +7,8 @@ public partial class EntryHandler [nameof(IEntry.Text)] = MapText, [nameof(IEntry.TextColor)] = MapTextColor, [nameof(IEntry.IsPassword)] = MapIsPassword, - [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled + [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, + [nameof(IEntry.IsReadOnly)] = MapIsReadOnly }; 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 6647a9027ae3..8825305ea08c 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -61,6 +61,11 @@ public static void MapIsTextPredictionEnabled(EntryHandler handler, IEntry entry handler.TypedNativeView?.UpdateIsTextPredictionEnabled(entry); } + public static void MapIsReadOnly(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateIsReadOnly(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 3111a347314b..719c46b8d9b2 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -57,11 +57,24 @@ internal static void SetInputType(this EditText editText, IEntry entry) if (!entry.IsTextPredictionEnabled && ((editText.InputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) editText.InputType |= InputTypes.TextFlagNoSuggestions; + + if (entry.IsReadOnly) + editText.InputType = InputTypes.Null; } public static void UpdateIsTextPredictionEnabled(this EditText editText, IEntry entry) { editText.SetInputType(entry); } + + public static void UpdateIsReadOnly(this EditText editText, IEntry entry) + { + bool isEditable = !entry.IsReadOnly; + + editText.SetInputType(entry); + + editText.FocusableInTouchMode = isEditable; + editText.Focusable = isEditable; + } } } diff --git a/src/Core/src/Platform/iOS/EntryExtensions.cs b/src/Core/src/Platform/iOS/EntryExtensions.cs index 6f0c2e5ec028..04f13d251980 100644 --- a/src/Core/src/Platform/iOS/EntryExtensions.cs +++ b/src/Core/src/Platform/iOS/EntryExtensions.cs @@ -45,5 +45,10 @@ public static void UpdateIsTextPredictionEnabled(this UITextField textField, IEn else textField.AutocorrectionType = UITextAutocorrectionType.No; } + + public static void UpdateIsReadOnly(this UITextField textField, IEntry entry) + { + textField.UserInteractionEnabled = !entry.IsReadOnly; + } } } \ 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 44c0b3617e9b..2834e47d35a0 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -31,5 +31,12 @@ bool GetNativeIsPassword(EntryHandler entryHandler) bool GetNativeIsTextPredictionEnabled(EntryHandler entryHandler) => !GetNativeEntry(entryHandler).InputType.HasFlag(InputTypes.TextFlagNoSuggestions); + + bool GetNativeIsReadOnly(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + + return !editText.Focusable && !editText.FocusableInTouchMode; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 9f044f384e9a..244e4fda3957 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -136,6 +136,23 @@ await ValidatePropertyUpdatesValue( unsetValue); } + [Theory(DisplayName = "IsReadOnly Updates Correctly")] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task IsReadOnlyUpdatesCorrectly(bool setValue, bool unsetValue) + { + var entry = new EntryStub(); + + await ValidatePropertyUpdatesValue( + entry, + nameof(IEntry.IsReadOnly), + GetNativeIsReadOnly, + setValue, + unsetValue); + } + [Theory(DisplayName = "Text Changed Events Fire Correctly")] // null/empty [InlineData(null, null, false)] diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs index 143f12bd891e..3becc6dfc2c8 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -22,5 +22,8 @@ bool GetNativeIsPassword(EntryHandler entryHandler) => bool GetNativeIsTextPredictionEnabled(EntryHandler entryHandler) => GetNativeEntry(entryHandler).AutocorrectionType == UITextAutocorrectionType.Yes; + + bool GetNativeIsReadOnly(EntryHandler entryHandler) => + !GetNativeEntry(entryHandler).UserInteractionEnabled; } } \ 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 86b468d615d6..7656b6850b7a 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -18,6 +18,8 @@ public string Text public bool IsTextPredictionEnabled { get; set; } + public bool IsReadOnly { get; set; } + public event EventHandler> TextChanged; void OnTextChanged(string oldValue, string newValue) =>