From b51a6a3dacaeffb3fdadefd69a612dc3d7f1717d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Su=C3=A1rez?= Date: Tue, 6 Apr 2021 05:35:57 +0200 Subject: [PATCH] Implement CharacterSpacing property in ButtonHandlers (#607) --- ...osoft.Maui.Controls.MultiTargeting.targets | 4 -- .../Android/FastRenderers/ButtonRenderer.cs | 1 + .../samples/Controls.Sample/Pages/MainPage.cs | 23 ++++++----- .../Handlers/Button/ButtonHandler.Android.cs | 28 +++++++++++--- .../Handlers/Button/ButtonHandler.Standard.cs | 1 + .../Handlers/Button/ButtonHandler.Windows.cs | 2 + src/Core/src/Handlers/Button/ButtonHandler.cs | 1 + .../src/Handlers/Button/ButtonHandler.iOS.cs | 6 +++ .../Handlers/Label/LabelHandler.Android.cs | 1 + .../src/Platform/Android/ButtonExtensions.cs | 26 ++++++++----- .../src/Platform/Windows/ControlExtensions.cs | 2 + src/Core/src/Platform/iOS/ButtonExtensions.cs | 24 ++++++++---- .../Button/ButtonHandlerTests.Android.cs | 38 +++++++++++++++++++ .../Handlers/Button/ButtonHandlerTests.iOS.cs | 34 +++++++++++++++++ 14 files changed, 156 insertions(+), 35 deletions(-) diff --git a/.nuspec/Microsoft.Maui.Controls.MultiTargeting.targets b/.nuspec/Microsoft.Maui.Controls.MultiTargeting.targets index e111d6aeb..a7901422b 100644 --- a/.nuspec/Microsoft.Maui.Controls.MultiTargeting.targets +++ b/.nuspec/Microsoft.Maui.Controls.MultiTargeting.targets @@ -32,10 +32,6 @@ - - - - diff --git a/src/Compatibility/Core/src/Android/FastRenderers/ButtonRenderer.cs b/src/Compatibility/Core/src/Android/FastRenderers/ButtonRenderer.cs index 649d3ea80..0b2aa41a3 100644 --- a/src/Compatibility/Core/src/Android/FastRenderers/ButtonRenderer.cs +++ b/src/Compatibility/Core/src/Android/FastRenderers/ButtonRenderer.cs @@ -361,6 +361,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers _textColorSwitcher.Value.UpdateTextColor(this, Button.TextColor); } + [PortHandler] void UpdateCharacterSpacing() { if (Forms.IsLollipopOrNewer) diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 6902ddcef..f7035d202 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -39,7 +39,6 @@ namespace Maui.Controls.Sample.Pages var verticalStack = new VerticalStackLayout() { Spacing = 5, BackgroundColor = Color.AntiqueWhite }; var horizontalStack = new HorizontalStackLayout() { Spacing = 2, BackgroundColor = Color.CornflowerBlue }; - verticalStack.Add(CreateSampleGrid()); verticalStack.Add(new Label { Text = " ", Padding = new Thickness(10) }); @@ -55,7 +54,7 @@ namespace Maui.Controls.Sample.Pages verticalStack.Add(new Label { Text = loremIpsum, MaxLines = 2 }); verticalStack.Add(new Label { Text = loremIpsum, LineBreakMode = LineBreakMode.TailTruncation }); verticalStack.Add(new Label { Text = loremIpsum, MaxLines = 2, LineBreakMode = LineBreakMode.TailTruncation }); - verticalStack.Add(new Label { Text = "This should have five times the line height!", LineHeight = 5 }); + verticalStack.Add(new Label { Text = "This should have five times the line height! " + loremIpsum, LineHeight = 5, MaxLines = 2 }); var visibleClearButtonEntry = new Entry() { ClearButtonVisibility = ClearButtonVisibility.WhileEditing, Placeholder = "This Entry will show clear button if has input." }; var hiddenClearButtonEntry = new Entry() { ClearButtonVisibility = ClearButtonVisibility.Never, Placeholder = "This Entry will not..." }; @@ -64,14 +63,6 @@ namespace Maui.Controls.Sample.Pages verticalStack.Add(hiddenClearButtonEntry); verticalStack.Add(new Editor { Placeholder = "This is an editor placeholder." }); - var paddingButton = new Button - { - Padding = new Thickness(40), - Text = "This button has a padding!!", - BackgroundColor = Color.Purple, - }; - - verticalStack.Add(paddingButton); var underlineLabel = new Label { Text = "underline", TextDecorations = TextDecorations.Underline }; verticalStack.Add(underlineLabel); @@ -99,10 +90,22 @@ namespace Maui.Controls.Sample.Pages horizontalStack.Add(button); horizontalStack.Add(button2); + horizontalStack.Add(new Label { Text = "And these buttons are in a HorizontalStackLayout", VerticalOptions = LayoutOptions.Center }); verticalStack.Add(horizontalStack); + var paddingButton = new Button + { + Padding = new Thickness(40), + Text = "This button has a padding!!", + BackgroundColor = Color.Purple, + }; + + verticalStack.Add(paddingButton); + verticalStack.Add(new Button { Text = "CharacterSpacing" }); + verticalStack.Add(new Button { CharacterSpacing = 8, Text = "CharacterSpacing" }); + verticalStack.Add(new CheckBox()); verticalStack.Add(new CheckBox { BackgroundColor = Color.LightPink }); verticalStack.Add(new CheckBox { IsChecked = true, Color = Color.Aquamarine }); diff --git a/src/Core/src/Handlers/Button/ButtonHandler.Android.cs b/src/Core/src/Handlers/Button/ButtonHandler.Android.cs index 56418db0c..79413fd3b 100644 --- a/src/Core/src/Handlers/Button/ButtonHandler.Android.cs +++ b/src/Core/src/Handlers/Button/ButtonHandler.Android.cs @@ -8,6 +8,8 @@ namespace Microsoft.Maui.Handlers { public partial class ButtonHandler : AbstractViewHandler { + static Thickness? DefaultPadding; + ButtonClickListener ClickListener { get; } = new ButtonClickListener(); ButtonTouchListener TouchListener { get; } = new ButtonTouchListener(); @@ -21,6 +23,17 @@ namespace Microsoft.Maui.Handlers return nativeButton; } + protected override void SetupDefaults(AppCompatButton nativeView) + { + DefaultPadding = new Thickness( + nativeView.PaddingLeft, + nativeView.PaddingTop, + nativeView.PaddingRight, + nativeView.PaddingBottom); + + base.SetupDefaults(nativeView); + } + protected override void ConnectHandler(AppCompatButton nativeView) { ClickListener.Handler = this; @@ -58,6 +71,11 @@ namespace Microsoft.Maui.Handlers handler.TypedNativeView?.UpdateTextColor(button); } + public static void MapCharacterSpacing(ButtonHandler handler, IButton button) + { + handler.TypedNativeView?.UpdateCharacterSpacing(button); + } + public static void MapFont(ButtonHandler handler, IButton button) { _ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class."); @@ -69,10 +87,10 @@ namespace Microsoft.Maui.Handlers public static void MapPadding(ButtonHandler handler, IButton button) { - handler.TypedNativeView?.UpdatePadding(button); + handler.TypedNativeView?.UpdatePadding(button, DefaultPadding); } - public bool OnTouch(IButton? button, AView? v, MotionEvent? e) + bool OnTouch(IButton? button, AView? v, MotionEvent? e) { switch (e?.ActionMasked) { @@ -87,12 +105,12 @@ namespace Microsoft.Maui.Handlers return false; } - public void OnClick(IButton? button, AView? v) + void OnClick(IButton? button, AView? v) { button?.Clicked(); } - public class ButtonClickListener : Java.Lang.Object, AView.IOnClickListener + class ButtonClickListener : Java.Lang.Object, AView.IOnClickListener { public ButtonHandler? Handler { get; set; } @@ -102,7 +120,7 @@ namespace Microsoft.Maui.Handlers } } - public class ButtonTouchListener : Java.Lang.Object, AView.IOnTouchListener + class ButtonTouchListener : Java.Lang.Object, AView.IOnTouchListener { public ButtonHandler? Handler { get; set; } diff --git a/src/Core/src/Handlers/Button/ButtonHandler.Standard.cs b/src/Core/src/Handlers/Button/ButtonHandler.Standard.cs index dc3b540c3..fb3b6a79a 100644 --- a/src/Core/src/Handlers/Button/ButtonHandler.Standard.cs +++ b/src/Core/src/Handlers/Button/ButtonHandler.Standard.cs @@ -9,6 +9,7 @@ namespace Microsoft.Maui.Handlers public static void MapBackgroundColor(ButtonHandler handler, IButton button) { } public static void MapText(ButtonHandler handler, IButton button) { } public static void MapTextColor(ButtonHandler handler, IButton button) { } + public static void MapCharacterSpacing(ButtonHandler handler, IButton button) { } public static void MapFont(ButtonHandler handler, IButton button) { } public static void MapPadding(ButtonHandler handler, IButton button) { } } diff --git a/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs b/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs index 3ddfdd1dd..3faafda7e 100644 --- a/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs +++ b/src/Core/src/Handlers/Button/ButtonHandler.Windows.cs @@ -59,6 +59,8 @@ namespace Microsoft.Maui.Handlers handler.TypedNativeView?.UpdateTextColor(button, DefaultForeground); } + public static void MapCharacterSpacing(ButtonHandler handler, IButton button) { } + public static void MapFont(ButtonHandler handler, IButton button) { _ = handler.Services ?? throw new InvalidOperationException($"{nameof(Services)} should have been set by base class."); diff --git a/src/Core/src/Handlers/Button/ButtonHandler.cs b/src/Core/src/Handlers/Button/ButtonHandler.cs index 6e6f27ca2..53ed2ed61 100644 --- a/src/Core/src/Handlers/Button/ButtonHandler.cs +++ b/src/Core/src/Handlers/Button/ButtonHandler.cs @@ -7,6 +7,7 @@ namespace Microsoft.Maui.Handlers [nameof(IButton.BackgroundColor)] = MapBackgroundColor, [nameof(IButton.Text)] = MapText, [nameof(IButton.TextColor)] = MapTextColor, + [nameof(IButton.CharacterSpacing)] = MapCharacterSpacing, [nameof(IButton.Font)] = MapFont, [nameof(IButton.Padding)] = MapPadding, }; diff --git a/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs b/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs index 32ad4fdda..5778fa7b2 100644 --- a/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs +++ b/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs @@ -59,6 +59,12 @@ namespace Microsoft.Maui.Handlers { handler.TypedNativeView?.UpdateTextColor(button, ButtonTextColorDefaultNormal, ButtonTextColorDefaultHighlighted, ButtonTextColorDefaultDisabled); } + + public static void MapCharacterSpacing(ButtonHandler handler, IButton button) + { + handler.TypedNativeView?.UpdateCharacterSpacing(button); + } + public static void MapPadding(ButtonHandler handler, IButton button) { handler.TypedNativeView?.UpdatePadding(button); diff --git a/src/Core/src/Handlers/Label/LabelHandler.Android.cs b/src/Core/src/Handlers/Label/LabelHandler.Android.cs index 0ff342b81..f0ff348f9 100644 --- a/src/Core/src/Handlers/Label/LabelHandler.Android.cs +++ b/src/Core/src/Handlers/Label/LabelHandler.Android.cs @@ -74,6 +74,7 @@ namespace Microsoft.Maui.Handlers handler.TypedNativeView?.UpdateFont(label, fontManager); } + public static void MapLineHeight(LabelHandler handler, ILabel label) { handler.TypedNativeView?.UpdateLineHeight(label, LineSpacingAddDefault, LineSpacingMultDefault); diff --git a/src/Core/src/Platform/Android/ButtonExtensions.cs b/src/Core/src/Platform/Android/ButtonExtensions.cs index 0267075e1..25de30555 100644 --- a/src/Core/src/Platform/Android/ButtonExtensions.cs +++ b/src/Core/src/Platform/Android/ButtonExtensions.cs @@ -24,6 +24,9 @@ namespace Microsoft.Maui public static void UpdateTextColor(this AppCompatButton appCompatButton, IButton button, XColor defaultColor) => appCompatButton.SetTextColor(button.TextColor.Cleanse(defaultColor).ToNative()); + public static void UpdateCharacterSpacing(this AppCompatButton appCompatButton, IButton button) => + appCompatButton.LetterSpacing = button.CharacterSpacing.ToEm(); + public static void UpdateFont(this AppCompatButton appCompatButton, IButton button, IFontManager fontManager) { var font = button.Font; @@ -35,21 +38,26 @@ namespace Microsoft.Maui appCompatButton.SetTextSize(ComplexUnitType.Sp, sp); } - public static void UpdatePadding(this AppCompatButton appCompatButton, IButton button) + public static void UpdatePadding(this AppCompatButton appCompatButton, IButton button, Thickness? defaultPadding = null) { var context = appCompatButton.Context; - if (context == null) - { return; - } - var padding = button.Padding; + + // TODO: have a way to use default padding + // Windows keeps the default as a base but this is also wrong. + // var padding = defaultPadding ?? new Thickness(); + var padding = new Thickness(); + padding.Left += context.ToPixels(button.Padding.Left); + padding.Top += context.ToPixels(button.Padding.Top); + padding.Right += context.ToPixels(button.Padding.Right); + padding.Bottom += context.ToPixels(button.Padding.Bottom); appCompatButton.SetPadding( - (int)context.ToPixels(padding.Left), - (int)context.ToPixels(padding.Top), - (int)context.ToPixels(padding.Right), - (int)context.ToPixels(padding.Bottom)); + (int)padding.Left, + (int)padding.Top, + (int)padding.Right, + (int)padding.Bottom); } static XColor Cleanse(this XColor color, XColor defaultColor) => color.IsDefault ? defaultColor : color; diff --git a/src/Core/src/Platform/Windows/ControlExtensions.cs b/src/Core/src/Platform/Windows/ControlExtensions.cs index 07c92b392..bdcb60536 100644 --- a/src/Core/src/Platform/Windows/ControlExtensions.cs +++ b/src/Core/src/Platform/Windows/ControlExtensions.cs @@ -23,6 +23,8 @@ namespace Microsoft.Maui public static void UpdatePadding(this Control nativeControl, Thickness padding, UI.Xaml.Thickness? defaultThickness = null) { + // TODO: have a way to reset the padding + // This is used for button, but this also means there can never be a 0 padding button var newPadding = defaultThickness ?? new UI.Xaml.Thickness(); newPadding.Left += padding.Left; diff --git a/src/Core/src/Platform/iOS/ButtonExtensions.cs b/src/Core/src/Platform/iOS/ButtonExtensions.cs index c7bb1962d..71582482f 100644 --- a/src/Core/src/Platform/iOS/ButtonExtensions.cs +++ b/src/Core/src/Platform/iOS/ButtonExtensions.cs @@ -1,4 +1,3 @@ -using Microsoft.Maui; using UIKit; namespace Microsoft.Maui @@ -8,8 +7,8 @@ namespace Microsoft.Maui public static void UpdateText(this UIButton nativeButton, IButton button) => nativeButton.SetTitle(button.Text, UIControlState.Normal); - public static void UpdateTextColor(this UIButton nativeButton, IButton button) - => nativeButton.UpdateTextColor(button); + public static void UpdateTextColor(this UIButton nativeButton, IButton button) => + nativeButton.UpdateTextColor(button); public static void UpdateTextColor(this UIButton nativeButton, IButton button, UIColor? buttonTextColorDefaultNormal, UIColor? buttonTextColorDefaultHighlighted, UIColor? buttonTextColorDefaultDisabled) { @@ -31,6 +30,17 @@ namespace Microsoft.Maui } } + public static void UpdateCharacterSpacing(this UIButton nativeButton, IButton button) + { + if (string.IsNullOrEmpty(button.Text)) + return; + + var textAttr = nativeButton.TitleLabel.AttributedText?.WithCharacterSpacing(button.CharacterSpacing); + + if (textAttr != null) + nativeButton.TitleLabel.AttributedText = textAttr; + } + public static void UpdateFont(this UIButton nativeButton, IButton button, IFontManager fontManager) { var uiFont = fontManager.GetFont(button.Font); @@ -40,10 +50,10 @@ namespace Microsoft.Maui public static void UpdatePadding(this UIButton nativeButton, IButton button) { nativeButton.ContentEdgeInsets = new UIEdgeInsets( - (float)button.Padding.Top, - (float)button.Padding.Left, - (float)button.Padding.Bottom, - (float)button.Padding.Right); + (float)button.Padding.Top, + (float)button.Padding.Left, + (float)button.Padding.Bottom, + (float)button.Padding.Right); } } } diff --git a/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.Android.cs index 47ba19daa..3616564a9 100644 --- a/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.Android.cs @@ -10,6 +10,32 @@ namespace Microsoft.Maui.DeviceTests { public partial class ButtonHandlerTests { + [Fact(DisplayName = "CharacterSpacing Initializes Correctly")] + public async Task CharacterSpacingInitializesCorrectly() + { + var xplatCharacterSpacing = 4; + + var button = new ButtonStub() + { + CharacterSpacing = xplatCharacterSpacing, + Text = "Test" + }; + + float expectedValue = button.CharacterSpacing.ToEm(); + + var values = await GetValueAsync(button, (handler) => + { + return new + { + ViewValue = button.CharacterSpacing, + NativeViewValue = GetNativeCharacterSpacing(handler) + }; + }); + + Assert.Equal(xplatCharacterSpacing, values.ViewValue); + Assert.Equal(expectedValue, values.NativeViewValue, EmCoefficientPrecision); + } + [Theory(DisplayName = "Font Family Initializes Correctly")] [InlineData(null)] [InlineData("monospace")] @@ -111,5 +137,17 @@ namespace Microsoft.Maui.DeviceTests bool GetNativeIsItalic(ButtonHandler buttonHandler) => GetNativeButton(buttonHandler).Typeface.IsItalic; + + double GetNativeCharacterSpacing(ButtonHandler buttonHandler) + { + var button = GetNativeButton(buttonHandler); + + if (button != null) + { + return button.LetterSpacing; + } + + return -1; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.iOS.cs index ab85f0035..63f1ac328 100644 --- a/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Button/ButtonHandlerTests.iOS.cs @@ -9,6 +9,31 @@ namespace Microsoft.Maui.DeviceTests { public partial class ButtonHandlerTests { + [Fact(DisplayName = "CharacterSpacing Initializes Correctly")] + public async Task CharacterSpacingInitializesCorrectly() + { + string originalText = "Test"; + var xplatCharacterSpacing = 4; + + var button = new ButtonStub() + { + CharacterSpacing = xplatCharacterSpacing, + Text = originalText + }; + + var values = await GetValueAsync(button, (handler) => + { + return new + { + ViewValue = button.CharacterSpacing, + NativeViewValue = GetNativeCharacterSpacing(handler) + }; + }); + + Assert.Equal(xplatCharacterSpacing, values.ViewValue); + Assert.Equal(xplatCharacterSpacing, values.NativeViewValue); + } + [Theory(DisplayName = "Font Family Initializes Correctly")] [InlineData(null)] [InlineData("Times New Roman")] @@ -79,5 +104,14 @@ namespace Microsoft.Maui.DeviceTests bool GetNativeIsItalic(ButtonHandler buttonHandler) => GetNativeButton(buttonHandler).TitleLabel.Font.FontDescriptor.SymbolicTraits.HasFlag(UIFontDescriptorSymbolicTraits.Italic); + + double GetNativeCharacterSpacing(ButtonHandler buttonHandler) + { + var button = GetNativeButton(buttonHandler); + + var attributedText = button.TitleLabel.AttributedText; + + return attributedText.GetCharacterSpacing(); + } } } \ No newline at end of file