diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index cf86328de43f..dc4c08b240b0 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -390,6 +390,7 @@ void UpdateMaxLength() EditText.Text = currentControlText.Substring(0, Element.MaxLength); } + [PortHandler] void UpdateCharacterSpacing() { if (Forms.IsLollipopOrNewer) diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 81f2059047fd..36b11b1fba75 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -375,6 +375,7 @@ void UpdateText() Control.Text = text; } + [PortHandler ("Partially ported ...")] void UpdateCharacterSpacing() { var textAttr = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing); diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 11439125a120..5ae4ac6c7f75 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -101,6 +101,7 @@ void SetupMauiLayout() 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 Entry { Text = "This should be text with character spacing", CharacterSpacing = 10 }); verticalStack.Add(new ProgressBar { Progress = 0.5 }); verticalStack.Add(new ProgressBar { Progress = 0.5, BackgroundColor = Color.LightCoral }); diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs index b219deb7d531..afca79255893 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -89,6 +89,11 @@ public static void MapReturnType(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateReturnType(entry); } + public static void MapCharacterSpacing(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateCharacterSpacing(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 26b0317259d6..8c9abd5c907c 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs @@ -16,5 +16,6 @@ public partial class EntryHandler : AbstractViewHandler public static void MapIsReadOnly(IViewHandler handler, IEntry entry) { } public static void MapFont(IViewHandler handler, IEntry entry) { } public static void MapReturnType(IViewHandler handler, IEntry entry) { } + public static void MapCharacterSpacing(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 7815e5be094c..a80eef6fd161 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -13,7 +13,8 @@ public partial class EntryHandler [nameof(IEntry.Placeholder)] = MapPlaceholder, [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, [nameof(IEntry.Font)] = MapFont, - [nameof(IEntry.ReturnType)] = MapReturnType + [nameof(IEntry.ReturnType)] = MapReturnType, + [nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing }; 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 362d224a86e9..3b04acfb3eaa 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -114,6 +114,11 @@ public static void MapFormatting(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateHorizontalTextAlignment(entry); } + public static void MapCharacterSpacing(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateCharacterSpacing(entry); + } + void OnEditingChanged(object? sender, EventArgs e) => OnTextChanged(); void OnEditingEnded(object? sender, EventArgs e) => OnTextChanged(); diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index 10a5fcdad32d..359af937e06b 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -139,6 +139,11 @@ public static void UpdateCharacterSpacing(this AppCompatEditText editText, IEdit editText.LetterSpacing = editor.CharacterSpacing.ToEm(); } + public static void UpdateCharacterSpacing(this AppCompatEditText editText, IEntry editor) + { + editText.LetterSpacing = editor.CharacterSpacing.ToEm(); + } + internal static void SetInputType(this AppCompatEditText editText, IEntry entry) { editText.InputType = InputTypes.ClassText; diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index 0decc64bfdc2..ef74895bd3b2 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -91,6 +91,14 @@ public static void UpdateCharacterSpacing(this UITextField textField, IText text textField.AttributedText = textAttr; } + public static void UpdateCharacterSpacing(this UITextField textField, IEntry textView) + { + var textAttr = textField.AttributedText?.WithCharacterSpacing(textView.CharacterSpacing); + + if (textAttr != null) + textField.AttributedText = textAttr; + } + public static void UpdateFont(this UITextField textField, IText textView, IFontManager fontManager) { var uiFont = fontManager.GetFont(textView.Font); diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 03dc95d340e3..c532b56cf17b 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -142,5 +142,43 @@ bool GetNativeIsReadOnly(EntryHandler entryHandler) ImeAction GetNativeReturnType(EntryHandler entryHandler) => GetNativeEntry(entryHandler).ImeOptions; + + [Fact(DisplayName = "CharacterSpacing Initializes Correctly")] + public async Task CharacterSpacingInitializesCorrectly() + { + var xplatCharacterSpacing = 4; + + var entry = new EntryStub() + { + CharacterSpacing = xplatCharacterSpacing, + Text = "Some Test Text" + }; + + float expectedValue = entry.CharacterSpacing.ToEm(); + + var values = await GetValueAsync(entry, (handler) => + { + return new + { + ViewValue = entry.CharacterSpacing, + NativeViewValue = GetNativeCharacterSpacing(handler) + }; + }); + + Assert.Equal(xplatCharacterSpacing, values.ViewValue); + Assert.Equal(expectedValue, values.NativeViewValue, EmCoefficientPrecision); + } + + double GetNativeCharacterSpacing(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + + if (editText != null) + { + return editText.LetterSpacing; + } + + return -1; + } } } \ 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 dd38f0be953a..1a06454e6876 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -85,6 +85,37 @@ public async Task ReturnTypeInitializesCorrectly() Assert.Equal(expectedValue, values.NativeViewValue); } + [Fact(DisplayName = "CharacterSpacing Initializes Correctly")] + public async Task CharacterSpacingInitializesCorrectly() + { + string originalText = "Some Test Text"; + var xplatCharacterSpacing = 4; + + var entry = new EntryStub() + { + CharacterSpacing = xplatCharacterSpacing, + Text = originalText + }; + + var values = await GetValueAsync(entry, (handler) => + { + return new + { + ViewValue = entry.CharacterSpacing, + NativeViewValue = GetNativeCharacterSpacing(handler) + }; + }); + + Assert.Equal(xplatCharacterSpacing, values.ViewValue); + Assert.Equal(xplatCharacterSpacing, values.NativeViewValue); + } + + double GetNativeCharacterSpacing(EntryHandler entryHandler) + { + var entry = GetNativeEntry(entryHandler); + return entry.AttributedText.GetCharacterSpacing(); + } + UITextField GetNativeEntry(EntryHandler entryHandler) => (UITextField)entryHandler.View;