diff --git a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs index 17d2248a499d..d599b63b1466 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs @@ -210,6 +210,7 @@ internal override void OnNativeFocusChanged(bool hasFocus) ElementController.SendCompleted(); } + [PortHandler] protected virtual void UpdateFont() { EditText.Typeface = Element.ToTypeface(); diff --git a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs index 400706464178..4c2e80ff2935 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs @@ -300,6 +300,7 @@ void UpdateEditable() TextView.InputAccessoryView.Hidden = !Element.IsEnabled; } + [PortHandler] protected internal virtual void UpdateFont() { var font = Element.ToUIFont(); diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 1d11beba2426..e0211b66a0dc 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -90,6 +90,7 @@ void SetupMauiLayout() verticalStack.Add(new Editor { Text = "Editor" }); verticalStack.Add(new Editor { Text = "Lorem ipsum dolor sit amet", MaxLength = 10 }); verticalStack.Add(new Editor { Text = "Predictive Text Off", IsTextPredictionEnabled = false }); + verticalStack.Add(new Editor { Text = "Lorem ipsum dolor sit amet", FontSize = 10, FontFamily = "dokdo_regular"}); var entry = new Entry(); entry.TextChanged += (sender, e) => diff --git a/src/Core/src/Handlers/Editor/EditorHandler.Android.cs b/src/Core/src/Handlers/Editor/EditorHandler.Android.cs index a89d90e92dc4..8f0e58914989 100644 --- a/src/Core/src/Handlers/Editor/EditorHandler.Android.cs +++ b/src/Core/src/Handlers/Editor/EditorHandler.Android.cs @@ -1,6 +1,8 @@ -using Android.Views; +using System; +using Android.Views; using Android.Views.InputMethods; using AndroidX.AppCompat.Widget; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Maui.Handlers { @@ -40,5 +42,14 @@ public static void MapIsTextPredictionEnabled(EditorHandler handler, IEditor edi { handler.TypedNativeView?.UpdateIsTextPredictionEnabled(editor); } + + public static void MapFont(EditorHandler handler, IEditor editor) + { + var services = handler.Services + ?? throw new InvalidOperationException($"Unable to find service provider, the handler.Services was null."); + var fontManager = services.GetRequiredService(); + + handler.TypedNativeView?.UpdateFont(editor, fontManager); + } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Editor/EditorHandler.Standard.cs b/src/Core/src/Handlers/Editor/EditorHandler.Standard.cs index 2f3167ce2c7e..722548b603c2 100644 --- a/src/Core/src/Handlers/Editor/EditorHandler.Standard.cs +++ b/src/Core/src/Handlers/Editor/EditorHandler.Standard.cs @@ -10,5 +10,6 @@ public static void MapText(IViewHandler handler, IEditor editor) { } public static void MapCharacterSpacing(IViewHandler handler, IEditor editor) { } public static void MapMaxLength(IViewHandler handler, IEditor editor) { } public static void MapIsTextPredictionEnabled(EditorHandler handler, IEditor editor) { } + public static void MapFont(IViewHandler handler, IEditor editor) { } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Editor/EditorHandler.cs b/src/Core/src/Handlers/Editor/EditorHandler.cs index 25caa2becb3f..b0fb4d9be7b2 100644 --- a/src/Core/src/Handlers/Editor/EditorHandler.cs +++ b/src/Core/src/Handlers/Editor/EditorHandler.cs @@ -7,7 +7,8 @@ public partial class EditorHandler [nameof(IEditor.Text)] = MapText, [nameof(IEditor.CharacterSpacing)] = MapCharacterSpacing, [nameof(IEditor.MaxLength)] = MapMaxLength, - [nameof(IEditor.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled + [nameof(IEditor.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, + [nameof(IEditor.Font)] = MapFont }; public EditorHandler() : base(EditorMapper) diff --git a/src/Core/src/Handlers/Editor/EditorHandler.iOS.cs b/src/Core/src/Handlers/Editor/EditorHandler.iOS.cs index 7faf1d38feb0..bd9bc039d2a8 100644 --- a/src/Core/src/Handlers/Editor/EditorHandler.iOS.cs +++ b/src/Core/src/Handlers/Editor/EditorHandler.iOS.cs @@ -1,5 +1,7 @@ -using CoreGraphics; +using CoreGraphics; using Foundation; +using System; +using Microsoft.Extensions.DependencyInjection; using UIKit; namespace Microsoft.Maui.Handlers @@ -75,5 +77,14 @@ bool OnShouldChangeText(UITextView textView, NSRange range, string replacementSt return newLength <= VirtualView.MaxLength; } + + public static void MapFont(EditorHandler handler, IEditor editor) + { + var services = handler.Services ?? + throw new InvalidOperationException($"Unable to find service provider, the handler.Services was null."); + var fontManager = services.GetRequiredService(); + + handler.TypedNativeView?.UpdateFont(editor, fontManager); + } } } \ 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 30a2d53db260..aa05e90df8b7 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -121,6 +121,17 @@ public static void UpdateReturnType(this AppCompatEditText editText, IEntry entr editText.ImeOptions = entry.ReturnType.ToNative(); } + public static void UpdateFont(this AppCompatEditText editText, IEditor editor, IFontManager fontManager) + { + var font = editor.Font; + + var tf = fontManager.GetTypeface(font); + editText.Typeface = tf; + + var sp = fontManager.GetScaledPixel(font); + editText.SetTextSize(Android.Util.ComplexUnitType.Sp, sp); + } + internal static void SetInputType(this AppCompatEditText editText, IEntry entry) { editText.InputType = InputTypes.ClassText; diff --git a/src/Core/src/Platform/iOS/TextViewExtensions.cs b/src/Core/src/Platform/iOS/TextViewExtensions.cs index fbb54e953ddf..84e91d6abf73 100644 --- a/src/Core/src/Platform/iOS/TextViewExtensions.cs +++ b/src/Core/src/Platform/iOS/TextViewExtensions.cs @@ -37,5 +37,11 @@ public static void UpdatePredictiveText(this UITextView textView, IEditor editor textView.AutocorrectionType = editor.IsTextPredictionEnabled ? UITextAutocorrectionType.Yes : UITextAutocorrectionType.No; } + + public static void UpdateFont(this UITextView textView, IEditor editor, IFontManager fontManager) + { + var uiFont = fontManager.GetFont(editor.Font); + textView.Font = uiFont; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.Android.cs index e48bc251bf1d..a57ffc3b6ea9 100644 --- a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.Android.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Android.Text; using AndroidX.AppCompat.Widget; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using Xunit; @@ -34,6 +35,33 @@ public async Task CharacterSpacingInitializesCorrectly() Assert.Equal(xplatCharacterSpacing, values.ViewValue); Assert.Equal(expectedValue, values.NativeViewValue, EmCoefficientPrecision); } + + [Theory(DisplayName = "Font Family Initializes Correctly")] + [InlineData(null)] + [InlineData("monospace")] + [InlineData("Dokdo")] + public async Task FontFamilyInitializesCorrectly(string family) + { + var editor = new EditorStub() + { + Text = "Test", + Font = Font.OfSize(family, 10) + }; + + var handler = await CreateHandlerAsync(editor); + var nativeEditor = GetNativeEditor(handler); + + var fontManager = handler.Services.GetRequiredService(); + + var nativeFont = fontManager.GetTypeface(Font.OfSize(family, 0.0)); + + Assert.Equal(nativeFont, nativeEditor.Typeface); + + if (string.IsNullOrEmpty(family)) + Assert.Equal(fontManager.DefaultTypeface, nativeEditor.Typeface); + else + Assert.NotEqual(fontManager.DefaultTypeface, nativeEditor.Typeface); + } AppCompatEditText GetNativeEditor(EditorHandler editorHandler) => (AppCompatEditText)editorHandler.View; @@ -55,5 +83,11 @@ double GetNativeCharacterSpacing(EditorHandler editorHandler) return -1; } + + double GetNativeUnscaledFontSize(EditorHandler editorHandler) + { + var textView = GetNativeEditor(editorHandler); + return textView.TextSize / textView.Resources.DisplayMetrics.Density; + } } -} \ No newline at end of file +} diff --git a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs index 6a39eb71b892..2b622af3f9ce 100644 --- a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.cs @@ -121,5 +121,21 @@ await ValidatePropertyUpdatesValue( setValue, unsetValue); } + + [Theory(DisplayName = "Font Size Initializes Correctly")] + [InlineData(1)] + [InlineData(10)] + [InlineData(20)] + [InlineData(100)] + public async Task FontSizeInitializesCorrectly(int fontSize) + { + var editor = new EditorStub() + { + Text = "Test", + Font = Font.OfSize("Arial", fontSize) + }; + + await ValidatePropertyInitValue(editor, () => editor.Font.FontSize, GetNativeUnscaledFontSize, editor.Font.FontSize); + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.iOS.cs index 87c308462b38..c481264375c3 100644 --- a/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Editor/EditorHandlerTests.iOS.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; using UIKit; @@ -33,6 +34,32 @@ public async Task CharacterSpacingInitializesCorrectly() Assert.Equal(xplatCharacterSpacing, values.NativeViewValue); } + [Theory(DisplayName = "Font Family Initializes Correctly")] + [InlineData(null)] + [InlineData("Times New Roman")] + [InlineData("Dokdo")] + public async Task FontFamilyInitializesCorrectly(string family) + { + var editor = new EditorStub() + { + Text = "Test", + Font = Font.OfSize(family, 10) + }; + + var handler = await CreateHandlerAsync(editor); + var nativeFont = await GetValueAsync(editor, handler => GetNativeEditor(handler).Font); + + var fontManager = handler.Services.GetRequiredService(); + + var expectedNativeFont = fontManager.GetFont(Font.OfSize(family, 0.0)); + + Assert.Equal(expectedNativeFont.FamilyName, nativeFont.FamilyName); + if (string.IsNullOrEmpty(family)) + Assert.Equal(fontManager.DefaultFont.FamilyName, nativeFont.FamilyName); + else + Assert.NotEqual(fontManager.DefaultFont.FamilyName, nativeFont.FamilyName); + } + UITextView GetNativeEditor(EditorHandler editorHandler) => (UITextView)editorHandler.View; @@ -47,5 +74,8 @@ double GetNativeCharacterSpacing(EditorHandler editorHandler) bool GetNativeIsTextPredictionEnabled(EditorHandler editorHandler) => GetNativeEditor(editorHandler).AutocorrectionType == UITextAutocorrectionType.Yes; + + double GetNativeUnscaledFontSize(EditorHandler editorHandler) => + GetNativeEditor(editorHandler).Font.PointSize; } } \ No newline at end of file