Skip to content

Commit

Permalink
Implement CharacterSpacing property in SearchBarHandlers (#494)
Browse files Browse the repository at this point in the history
* Implement CharacterSpacing in SearchBarHandlers

* Tests failing because of static

* Rounding for Android

* Fix iOS test

* There! No magic numbers!

* Fix rebase issuse and make SearchBar stuff work on iOS

Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
Co-authored-by: E.Z. Hart <hartez@gmail.com>
  • Loading branch information
3 people committed Mar 17, 2021
1 parent aeb181a commit 7c3a94c
Show file tree
Hide file tree
Showing 19 changed files with 134 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ void UpdateText()
Control.SetQuery(text, false);
}

[PortHandler]
void UpdateCharacterSpacing()
{
if (!Forms.IsLollipopOrNewer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ void OnTextChanged(object sender, UISearchBarTextChangedEventArgs a)
UpdateOnTextChanged();
}

[PortHandler("The code related to Placeholder remains to be ported")]
void UpdateCharacterSpacing()
{
_textField = _textField ?? Control.FindDescendantView<UITextField>();
Expand Down
6 changes: 5 additions & 1 deletion src/Controls/samples/Controls.Sample/Pages/MainPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ void SetupMauiLayout()
var horizontalStack = new HorizontalStackLayout() { Spacing = 2, BackgroundColor = Color.CornflowerBlue };

verticalStack.Add(new Label { Text = " ", Padding = new Thickness(10) });

var label = new Label { Text = "End-aligned text", BackgroundColor = Color.Fuchsia, HorizontalTextAlignment = TextAlignment.End };
label.Margin = new Thickness(15, 10, 20, 15);

Expand Down Expand Up @@ -105,6 +104,11 @@ void SetupMauiLayout()
verticalStack.Add(new ProgressBar { Progress = 0.5, BackgroundColor = Color.LightCoral });
verticalStack.Add(new ProgressBar { Progress = 0.5, ProgressColor = Color.Purple });

var searchBar = new SearchBar();
searchBar.CharacterSpacing = 4;
searchBar.Text = "A search query";
verticalStack.Add(searchBar);

var searchBar = new SearchBar();
searchBar.Text = "A search query";
verticalStack.Add(searchBar);
Expand Down
5 changes: 5 additions & 0 deletions src/Core/src/Core/ISearchBar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ public interface ISearchBar : IView, IPlaceholder, ITextAlignment
/// Gets a string containing the query text in the SearchBar.
/// </summary>
string Text { get; }

/// <summary>
/// Gets a string containing the query text in the SearchBar.
/// </summary>
double CharacterSpacing { get; }
}
}
7 changes: 6 additions & 1 deletion src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@ public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar
{
handler.TypedNativeView?.UpdatePlaceholder(searchBar);
}

public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar)
{
handler.QueryEditor?.UpdateHorizontalTextAlignment(searchBar);
}

public static void MapCharacterSpacing(SearchBarHandler handler, ISearchBar searchBar)
{
handler.QueryEditor?.UpdateCharacterSpacing(searchBar);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, object>
public static void MapText(IViewHandler handler, ISearchBar searchBar) { }
public static void MapPlaceholder(IViewHandler handler, ISearchBar searchBar) { }
public static void MapHorizontalTextAlignment(IViewHandler handler, ISearchBar searchBar) { }
public static void MapCharacterSpacing(IViewHandler handler, ISearchBar searchBar) { }
}
}
3 changes: 2 additions & 1 deletion src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ public partial class SearchBarHandler
{
[nameof(ISearchBar.Text)] = MapText,
[nameof(ISearchBar.Placeholder)] = MapPlaceholder,
[nameof(ISearchBar.HorizontalTextAlignment)] = MapHorizontalTextAlignment
[nameof(ISearchBar.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
[nameof(ISearchBar.CharacterSpacing)] = MapCharacterSpacing
};

public SearchBarHandler() : base(SearchBarMapper)
Expand Down
15 changes: 11 additions & 4 deletions src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
using System.Drawing;
using UIKit;

namespace Microsoft.Maui.Handlers
{
public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, UISearchBar>
{
UITextField? _textField;
UITextField? _editor;
public UITextField? QueryEditor => _editor;

protected override UISearchBar CreateNativeView()
{
var searchBar = new UISearchBar();
var searchBar = new UISearchBar(RectangleF.Empty) { ShowsCancelButton = true, BarStyle = UIBarStyle.Default };

_textField = searchBar.FindDescendantView<UITextField>();
_editor = searchBar.FindDescendantView<UITextField>();

return searchBar;
}
Expand All @@ -27,7 +29,12 @@ public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar

public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar)
{
handler.TypedNativeView?.UpdateHorizontalTextAlignment(searchBar, handler._textField);
handler.QueryEditor?.UpdateHorizontalTextAlignment(searchBar);
}

public static void MapCharacterSpacing(SearchBarHandler handler, ISearchBar searchBar)
{
handler.QueryEditor?.UpdateCharacterSpacing(searchBar);
}
}
}
3 changes: 3 additions & 0 deletions src/Core/src/Platform/Android/TextViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public static void UpdateTextColor(this TextView textView, ILabel label, Color d
public static void UpdateCharacterSpacing(this TextView textView, ILabel label) =>
textView.LetterSpacing = label.CharacterSpacing.ToEm();

public static void UpdateCharacterSpacing(this TextView textView, ISearchBar searchBar) =>
textView.LetterSpacing = searchBar.CharacterSpacing.ToEm();

public static void UpdateFont(this TextView textView, ILabel label, IFontManager fontManager)
{
var font = label.Font;
Expand Down
2 changes: 1 addition & 1 deletion src/Core/src/Platform/Android/UnitExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[PortHandler]
public static class UnitExtensions
{
public static float EmCoefficient = 0.0624f;
public const float EmCoefficient = 0.0624f;

public static float ToEm(this double pt)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Core/src/Platform/iOS/EntryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ public static void UpdateIsPassword(this UITextField textField, IEntry entry)
textField.SecureTextEntry = entry.IsPassword;
}

public static void UpdateHorizontalTextAlignment(this UITextField textField, IEntry entry)
public static void UpdateHorizontalTextAlignment(this UITextField textField, ITextAlignment textAlignment)
{
// 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 = entry.HorizontalTextAlignment.ToNative(true);
textField.TextAlignment = textAlignment.HorizontalTextAlignment.ToNative(true);
}

public static void UpdateIsTextPredictionEnabled(this UITextField textField, IEntry entry)
Expand Down
18 changes: 0 additions & 18 deletions src/Core/src/Platform/iOS/SearchBarExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,5 @@ public static void UpdatePlaceholder(this UISearchBar uiSearchBar, ISearchBar se
{
uiSearchBar.Placeholder = searchBar.Placeholder;
}

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<UITextField>();

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);
}
}
}
17 changes: 17 additions & 0 deletions src/Core/src/Platform/iOS/TextFieldExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using UIKit;

namespace Microsoft.Maui
{
public static class TextFieldExtensions
{
public static void UpdateCharacterSpacing(this UITextField textField, ISearchBar searchBar)
{
var textAttr = textField.AttributedText?.AddCharacterSpacing(searchBar.Text, searchBar.CharacterSpacing);

if (textAttr != null)
textField.AttributedText = textAttr;

// TODO: Include AttributedText to Label Placeholder
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ Task ValidateNativeBackgroundColor(ILabel label, Color color)
}

double GetNativeCharacterSpacing(LabelHandler labelHandler) =>
Math.Round(GetNativeLabel(labelHandler).LetterSpacing / UnitExtensions.EmCoefficient, 4);
Math.Round(GetNativeLabel(labelHandler).LetterSpacing / UnitExtensions.EmCoefficient, EmCoefficientPrecision);

TextUtils.TruncateAt GetNativeLineBreakMode(LabelHandler labelHandler) =>
GetNativeLabel(labelHandler).Ellipsize;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,7 @@ double GetNativeCharacterSpacing(LabelHandler labelHandler)
{
var nativeLabel = GetNativeLabel(labelHandler);
var text = nativeLabel.AttributedText;
if (text == null)
return 0;

var value = text.GetAttribute(UIStringAttributeKey.KerningAdjustment, 0, out var range);
if (value == null)
return 0;

Assert.Equal(0, range.Location);
Assert.Equal(text.Length, range.Length);

var kerning = Assert.IsType<NSNumber>(value);
return kerning.DoubleValue;
return text.GetCharacterSpacing();
}

UITextAlignment GetNativeTextAlignment(LabelHandler labelHandler) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Linq;
using System.Threading.Tasks;
using Android.Widget;
using Android.Widget;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Handlers;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using SearchView = AndroidX.AppCompat.Widget.SearchView;

Expand Down Expand Up @@ -36,6 +36,32 @@ public async Task HorizontalTextAlignmentInitializesCorrectly()
values.NativeViewValue.AssertHasFlag(expectedValue);
}

[Fact(DisplayName = "CharacterSpacing Initializes Correctly")]
public async Task CharacterSpacingInitializesCorrectly()
{
var xplatCharacterSpacing = 4;

var searchBar = new SearchBarStub()
{
CharacterSpacing = xplatCharacterSpacing,
Text = "Test"
};

float expectedValue = searchBar.CharacterSpacing.ToEm();

var values = await GetValueAsync(searchBar, (handler) =>
{
return new
{
ViewValue = searchBar.CharacterSpacing,
NativeViewValue = GetNativeCharacterSpacing(handler)
};
});

Assert.Equal(xplatCharacterSpacing, values.ViewValue);
Assert.Equal(expectedValue, values.NativeViewValue, EmCoefficientPrecision);
}

SearchView GetNativeSearchBar(SearchBarHandler searchBarHandler) =>
(SearchView)searchBarHandler.View;

Expand All @@ -45,14 +71,23 @@ string GetNativeText(SearchBarHandler searchBarHandler) =>
string GetNativePlaceholder(SearchBarHandler searchBarHandler) =>
GetNativeSearchBar(searchBarHandler).QueryHint;

Android.Views.TextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHandler)
double GetNativeCharacterSpacing(SearchBarHandler searchBarHandler)
{
var searchView = GetNativeSearchBar(searchBarHandler);
var editText = searchView.GetChildrenOfType<EditText>().FirstOrDefault();

if (editText == null)
return Android.Views.TextAlignment.Inherit;
if (editText != null)
{
return editText.LetterSpacing;
}

return -1;
}

Android.Views.TextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHandler)
{
var searchView = GetNativeSearchBar(searchBarHandler);
var editText = searchView.GetChildrenOfType<EditText>().First();
return editText.TextAlignment;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ public async Task PlaceholderInitializesCorrectly()
await ValidatePropertyInitValue(searchBar, () => searchBar.Placeholder, GetNativePlaceholder, searchBar.Placeholder);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,31 @@ public async Task HorizontalTextAlignmentInitializesCorrectly()
Assert.Equal(xplatHorizontalTextAlignment, values.ViewValue);
values.NativeViewValue.AssertHasFlag(expectedValue);
}

[Fact(DisplayName = "CharacterSpacing Initializes Correctly")]
public async Task CharacterSpacingInitializesCorrectly()
{
string originalText = "Test";
var xplatCharacterSpacing = 4;

var slider = new SearchBarStub()
{
CharacterSpacing = xplatCharacterSpacing,
Text = originalText
};

var values = await GetValueAsync(slider, (handler) =>
{
return new
{
ViewValue = slider.CharacterSpacing,
NativeViewValue = GetNativeCharacterSpacing(handler)
};
});

Assert.Equal(xplatCharacterSpacing, values.ViewValue);
Assert.Equal(xplatCharacterSpacing, values.NativeViewValue);
}

UISearchBar GetNativeSearchBar(SearchBarHandler searchBarHandler) =>
(UISearchBar)searchBarHandler.View;
Expand All @@ -53,5 +78,13 @@ UITextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHandler)

return textField.TextAlignment;
}

double GetNativeCharacterSpacing(SearchBarHandler searchBarHandler)
{
var searchBar = GetNativeSearchBar(searchBarHandler);
var textField = searchBar.FindDescendantView<UITextField>();

return textField.AttributedText.GetCharacterSpacing();
}
}
}
2 changes: 2 additions & 0 deletions src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public partial class SearchBarStub : StubBase, ISearchBar
public string Placeholder { get; set; }

public TextAlignment HorizontalTextAlignment { get; set; }

public double CharacterSpacing { get; set; }
}
}

0 comments on commit 7c3a94c

Please sign in to comment.