diff --git a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs index 2d264406a515..bdf2336ffe7a 100644 --- a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs @@ -233,6 +233,7 @@ void ClearFocus(SearchView view) view.ClearFocus(); } + [PortHandler] void UpdateFont() { _editText = _editText ?? Control.GetChildrenOfType().FirstOrDefault(); diff --git a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs index 10c46ddeb4d4..90825cdd5068 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs @@ -291,6 +291,7 @@ public virtual void UpdateCancelButton() } } + [PortHandler] void UpdateFont() { _textField = _textField ?? Control.FindDescendantView(); diff --git a/src/Core/src/Core/ISearchBar.cs b/src/Core/src/Core/ISearchBar.cs index df1d4a2d7f4c..b49bf06fa6d8 100644 --- a/src/Core/src/Core/ISearchBar.cs +++ b/src/Core/src/Core/ISearchBar.cs @@ -3,16 +3,8 @@ /// /// Represents a View used to initiating a search. /// - public interface ISearchBar : IView, IPlaceholder, ITextAlignment + public interface ISearchBar : IView, IText, IPlaceholder, ITextAlignment { - /// - /// Gets a string containing the query text in the SearchBar. - /// - string Text { get; } - - /// - /// Gets a string containing the query text in the SearchBar. - /// - double CharacterSpacing { get; } + } } \ No newline at end of file diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs index 6a70878c9244..ee8c1b3a2fd0 100644 --- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs +++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System; +using System.Linq; using Android.Widget; +using Microsoft.Extensions.DependencyInjection; using SearchView = AndroidX.AppCompat.Widget.SearchView; namespace Microsoft.Maui.Handlers @@ -28,6 +30,15 @@ public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar handler.TypedNativeView?.UpdatePlaceholder(searchBar); } + public static void MapFont(SearchBarHandler handler, ISearchBar searchBar) + { + var services = App.Current?.Services + ?? throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null."); + var fontManager = services.GetRequiredService(); + + handler.TypedNativeView?.UpdateFont(searchBar, fontManager, handler._editText); + } + public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar) { handler.QueryEditor?.UpdateHorizontalTextAlignment(searchBar); diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.Standard.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.Standard.cs index 1cfa95895de7..278892502e92 100644 --- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.Standard.cs +++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.Standard.cs @@ -8,6 +8,7 @@ public partial class SearchBarHandler : AbstractViewHandler public static void MapText(IViewHandler handler, ISearchBar searchBar) { } public static void MapPlaceholder(IViewHandler handler, ISearchBar searchBar) { } + public static void MapFont(IViewHandler handler, ISearchBar searchBar) { } public static void MapHorizontalTextAlignment(IViewHandler handler, ISearchBar searchBar) { } public static void MapCharacterSpacing(IViewHandler handler, ISearchBar searchBar) { } } diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs index a5599a757ef1..ee7ff98a406e 100644 --- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs +++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.cs @@ -6,6 +6,7 @@ public partial class SearchBarHandler { [nameof(ISearchBar.Text)] = MapText, [nameof(ISearchBar.Placeholder)] = MapPlaceholder, + [nameof(ISearchBar.Font)] = MapFont, [nameof(ISearchBar.HorizontalTextAlignment)] = MapHorizontalTextAlignment, [nameof(ISearchBar.CharacterSpacing)] = MapCharacterSpacing }; diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs index fc50d380f966..39e65ccab386 100644 --- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs +++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs @@ -1,4 +1,6 @@ +using System; using System.Drawing; +using Microsoft.Extensions.DependencyInjection; using UIKit; namespace Microsoft.Maui.Handlers @@ -27,6 +29,17 @@ public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar handler.TypedNativeView?.UpdatePlaceholder(searchBar); } + // TODO ezhart The extension methods are handling the dependent property updates; those should be in the mapping + + public static void MapFont(SearchBarHandler handler, ISearchBar searchBar) + { + var services = App.Current?.Services ?? + throw new InvalidOperationException($"Unable to find service provider, the App.Current.Services was null."); + var fontManager = services.GetRequiredService(); + + handler.QueryEditor?.UpdateFont(searchBar, fontManager); + } + public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar) { handler.QueryEditor?.UpdateHorizontalTextAlignment(searchBar); diff --git a/src/Core/src/Platform/Android/SearchBarExtensions.cs b/src/Core/src/Platform/Android/SearchBarExtensions.cs index 480a792051de..cb89ea5d7c0c 100644 --- a/src/Core/src/Platform/Android/SearchBarExtensions.cs +++ b/src/Core/src/Platform/Android/SearchBarExtensions.cs @@ -1,4 +1,7 @@ -using SearchView = AndroidX.AppCompat.Widget.SearchView; +using System.Linq; +using Android.Util; +using Android.Widget; +using SearchView = AndroidX.AppCompat.Widget.SearchView; namespace Microsoft.Maui { @@ -13,5 +16,26 @@ public static void UpdatePlaceholder(this SearchView searchView, ISearchBar sear { searchView.QueryHint = searchBar.Placeholder; } + + public static void UpdateFont(this SearchView searchView, ISearchBar searchBar, IFontManager fontManager) + { + searchView.UpdateFont(searchBar, fontManager, null); + } + + public static void UpdateFont(this SearchView searchView, ISearchBar searchBar, IFontManager fontManager, EditText? editText) + { + editText ??= searchView.GetChildrenOfType().FirstOrDefault(); + + if (editText == null) + return; + + var font = searchBar.Font; + + var tf = fontManager.GetTypeface(font); + editText.Typeface = tf; + + var sp = fontManager.GetScaledPixel(font); + editText.SetTextSize(ComplexUnitType.Sp, sp); + } } } diff --git a/src/Core/src/Platform/iOS/LabelExtensions.cs b/src/Core/src/Platform/iOS/LabelExtensions.cs index dd8f2aadd3db..a5afe62d3015 100644 --- a/src/Core/src/Platform/iOS/LabelExtensions.cs +++ b/src/Core/src/Platform/iOS/LabelExtensions.cs @@ -79,6 +79,32 @@ public static void UpdatePadding(this MauiLabel nativeLabel, ILabel label) (float)label.Padding.Right); } + public static void UpdateTextDecorations(this UILabel nativeLabel, ILabel label) + { + if (nativeLabel.AttributedText != null && !(nativeLabel.AttributedText?.Length > 0)) + return; + + var textDecorations = label?.TextDecorations; + + var newAttributedText = nativeLabel.AttributedText != null ? new NSMutableAttributedString(nativeLabel.AttributedText) : new NSMutableAttributedString(label?.Text ?? string.Empty); + var strikeThroughStyleKey = UIStringAttributeKey.StrikethroughStyle; + var underlineStyleKey = UIStringAttributeKey.UnderlineStyle; + + var range = new NSRange(0, newAttributedText.Length); + + if ((textDecorations & TextDecorations.Strikethrough) == 0) + newAttributedText.RemoveAttribute(strikeThroughStyleKey, range); + else + newAttributedText.AddAttribute(strikeThroughStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range); + + if ((textDecorations & TextDecorations.Underline) == 0) + newAttributedText.RemoveAttribute(underlineStyleKey, range); + else + newAttributedText.AddAttribute(underlineStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range); + + nativeLabel.AttributedText = newAttributedText; + } + internal static void SetLineBreakMode(this UILabel nativeLabel, ILabel label) { int maxLines = label.MaxLines; @@ -113,31 +139,5 @@ internal static void SetLineBreakMode(this UILabel nativeLabel, ILabel label) nativeLabel.Lines = maxLines; } - - public static void UpdateTextDecorations(this UILabel nativeLabel, ILabel label) - { - if (nativeLabel.AttributedText != null && !(nativeLabel.AttributedText?.Length > 0)) - return; - - var textDecorations = label?.TextDecorations; - - var newAttributedText = nativeLabel.AttributedText != null ? new NSMutableAttributedString(nativeLabel.AttributedText) : new NSMutableAttributedString(label?.Text ?? string.Empty); - var strikeThroughStyleKey = UIStringAttributeKey.StrikethroughStyle; - var underlineStyleKey = UIStringAttributeKey.UnderlineStyle; - - var range = new NSRange(0, newAttributedText.Length); - - if ((textDecorations & TextDecorations.Strikethrough) == 0) - newAttributedText.RemoveAttribute(strikeThroughStyleKey, range); - else - newAttributedText.AddAttribute(strikeThroughStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range); - - if ((textDecorations & TextDecorations.Underline) == 0) - newAttributedText.RemoveAttribute(underlineStyleKey, range); - else - newAttributedText.AddAttribute(underlineStyleKey, NSNumber.FromInt32((int)NSUnderlineStyle.Single), range); - - nativeLabel.AttributedText = newAttributedText; - } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/SearchBarExtensions.cs b/src/Core/src/Platform/iOS/SearchBarExtensions.cs index 097adce5401a..8a4928279f0b 100644 --- a/src/Core/src/Platform/iOS/SearchBarExtensions.cs +++ b/src/Core/src/Platform/iOS/SearchBarExtensions.cs @@ -13,5 +13,39 @@ public static void UpdatePlaceholder(this UISearchBar uiSearchBar, ISearchBar se { uiSearchBar.Placeholder = searchBar.Placeholder; } + + public static void UpdateFont(this UISearchBar uiSearchBar, ISearchBar searchBar, IFontManager fontManager) + { + uiSearchBar.UpdateFont(searchBar, fontManager, null); + } + + public static void UpdateFont(this UISearchBar uiSearchBar, ISearchBar searchBar, IFontManager fontManager, UITextField? textField) + { + textField ??= uiSearchBar.FindDescendantView(); + + if (textField == null) + return; + + var uiFont = fontManager.GetFont(searchBar.Font); + textField.Font = uiFont; + } + + //public static void UpdateHorizontalTextAlignment(this UISearchBar uiSearchBar, ISearchBar searchBar) + //{ + // UpdateHorizontalTextAlignment(uiSearchBar, searchBar, null); + //} + + //public static void UpdateHorizontalTextAlignment(this UISearchBar uiSearchBar, ISearchBar searchBar, UITextField? textField) + //{ + // textField ??= uiSearchBar.FindDescendantView(); + + // if (textField == null) + // return; + + // // We don't have a FlowDirection yet, so there's nothing to pass in here. + // // TODO: Update this when FlowDirection is available + // // (or update the extension to take an ILabel instead of an alignment and work it out from there) + // textField.TextAlignment = searchBar.HorizontalTextAlignment.ToNative(true); + //} } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index a5b19126346c..dd2be9ad77f2 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -13,5 +13,13 @@ public static void UpdateCharacterSpacing(this UITextField textField, ISearchBar // TODO: Include AttributedText to Label Placeholder } + + public static void UpdateFont(this UITextField textField, ISearchBar searchBar, IFontManager fontManager) + { + var uiFont = fontManager.GetFont(searchBar.Font); + textField.Font = uiFont; + + textField.UpdateCharacterSpacing(searchBar); + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.Android.cs index a3c56622d60e..9e643df34d14 100644 --- a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.Android.cs @@ -90,5 +90,38 @@ Android.Views.TextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHan var editText = searchView.GetChildrenOfType().First(); return editText.TextAlignment; } + + double GetNativeUnscaledFontSize(SearchBarHandler searchBarHandler) + { + var searchView = GetNativeSearchBar(searchBarHandler); + var editText = searchView.GetChildrenOfType().FirstOrDefault(); + + if (editText == null) + return -1; + + return editText.TextSize / editText.Resources.DisplayMetrics.Density; + } + + bool GetNativeIsBold(SearchBarHandler searchBarHandler) + { + var searchView = GetNativeSearchBar(searchBarHandler); + var editText = searchView.GetChildrenOfType().FirstOrDefault(); + + if (editText == null) + return false; + + return editText.Typeface.IsBold; + } + + bool GetNativeIsItalic(SearchBarHandler searchBarHandler) + { + var searchView = GetNativeSearchBar(searchBarHandler); + var editText = searchView.GetChildrenOfType().FirstOrDefault(); + + if (editText == null) + return false; + + return editText.Typeface.IsItalic; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.cs index be9fcddf885f..e1d70393742a 100644 --- a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.cs @@ -56,5 +56,38 @@ public async Task PlaceholderInitializesCorrectly() await ValidatePropertyInitValue(searchBar, () => searchBar.Placeholder, GetNativePlaceholder, searchBar.Placeholder); } + + [Theory(DisplayName = "Font Size Initializes Correctly")] + [InlineData(1)] + [InlineData(10)] + [InlineData(20)] + [InlineData(100)] + public async Task FontSizeInitializesCorrectly(int fontSize) + { + var searchBar = new SearchBarStub() + { + Text = "Test", + Font = Font.OfSize("Arial", fontSize) + }; + + await ValidatePropertyInitValue(searchBar, () => searchBar.Font.FontSize, GetNativeUnscaledFontSize, searchBar.Font.FontSize); + } + + [Theory(DisplayName = "Font Attributes Initialize Correctly")] + [InlineData(FontAttributes.None, false, false)] + [InlineData(FontAttributes.Bold, true, false)] + [InlineData(FontAttributes.Italic, false, true)] + [InlineData(FontAttributes.Bold | FontAttributes.Italic, true, true)] + public async Task FontAttributesInitializeCorrectly(FontAttributes attributes, bool isBold, bool isItalic) + { + var searchBar = new SearchBarStub() + { + Text = "Test", + Font = Font.OfSize("Arial", 10).WithAttributes(attributes) + }; + + await ValidatePropertyInitValue(searchBar, () => searchBar.Font.FontAttributes.HasFlag(FontAttributes.Bold), GetNativeIsBold, isBold); + await ValidatePropertyInitValue(searchBar, () => searchBar.Font.FontAttributes.HasFlag(FontAttributes.Italic), GetNativeIsItalic, isItalic); + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs index 6b2a5bd2db08..cabf787647d7 100644 --- a/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/SearchBar/SearchBarHandlerTests.iOS.cs @@ -86,5 +86,38 @@ double GetNativeCharacterSpacing(SearchBarHandler searchBarHandler) return textField.AttributedText.GetCharacterSpacing(); } + + double GetNativeUnscaledFontSize(SearchBarHandler searchBarHandler) + { + var uiSearchBar = GetNativeSearchBar(searchBarHandler); + var textField = uiSearchBar.FindDescendantView(); + + if (textField == null) + return -1; + + return textField.Font.PointSize; + } + + bool GetNativeIsBold(SearchBarHandler searchBarHandler) + { + var uiSearchBar = GetNativeSearchBar(searchBarHandler); + var textField = uiSearchBar.FindDescendantView(); + + if (textField == null) + return false; + + return textField.Font.FontDescriptor.SymbolicTraits.HasFlag(UIFontDescriptorSymbolicTraits.Bold); + } + + bool GetNativeIsItalic(SearchBarHandler searchBarHandler) + { + var uiSearchBar = GetNativeSearchBar(searchBarHandler); + var textField = uiSearchBar.FindDescendantView(); + + if (textField == null) + return false; + + return textField.Font.FontDescriptor.SymbolicTraits.HasFlag(UIFontDescriptorSymbolicTraits.Italic); + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs b/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs index 70a6b7f824c6..8991b64b45d9 100644 --- a/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs @@ -8,8 +8,12 @@ public partial class SearchBarStub : StubBase, ISearchBar public string Placeholder { get; set; } - public TextAlignment HorizontalTextAlignment { get; set; } + public Color TextColor { get; set; } public double CharacterSpacing { get; set; } + + public Font Font { get; set; } + + public TextAlignment HorizontalTextAlignment { get; set; } } } \ No newline at end of file