Implement HorizontalTextAlignment in SearchBarHandlers (#508)

* Implement HorizontalTextAlignment in SearchBarHandlers

* Clean up, add comments, verify things work with and without RTL support on Android

Co-authored-by: E.Z. Hart <hartez@gmail.com>
This commit is contained in:
Javier Suárez 2021-03-16 01:05:13 +01:00 коммит произвёл GitHub
Родитель 667eb0391b
Коммит 4e01228c45
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 188 добавлений и 15 удалений

Просмотреть файл

@ -4,6 +4,7 @@ using AGravityFlags = Android.Views.GravityFlags;
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{ {
[PortHandler]
internal static class TextAlignmentExtensions internal static class TextAlignmentExtensions
{ {
internal static void UpdateHorizontalAlignment(this EditText view, TextAlignment alignment, bool hasRtlSupport, AGravityFlags orMask = AGravityFlags.NoGravity) internal static void UpdateHorizontalAlignment(this EditText view, TextAlignment alignment, bool hasRtlSupport, AGravityFlags orMask = AGravityFlags.NoGravity)

Просмотреть файл

@ -172,6 +172,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
ClearFocus(Control); ClearFocus(Control);
} }
[PortHandler]
void UpdateHorizontalTextAlignment() void UpdateHorizontalTextAlignment()
{ {
_editText = _editText ?? Control.GetChildrenOfType<EditText>().FirstOrDefault(); _editText = _editText ?? Control.GetChildrenOfType<EditText>().FirstOrDefault();

Просмотреть файл

@ -236,6 +236,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
_textField.AttributedPlaceholder = _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); _textField.AttributedPlaceholder = _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing);
} }
[PortHandler]
void UpdateHorizontalTextAlignment() void UpdateHorizontalTextAlignment()
{ {
_textField = _textField ?? Control.FindDescendantView<UITextField>(); _textField = _textField ?? Control.FindDescendantView<UITextField>();

Просмотреть файл

@ -3,7 +3,7 @@
/// <summary> /// <summary>
/// Represents a View used to initiating a search. /// Represents a View used to initiating a search.
/// </summary> /// </summary>
public interface ISearchBar : IView, IPlaceholder public interface ISearchBar : IView, IPlaceholder, ITextAlignment
{ {
/// <summary> /// <summary>
/// Gets a string containing the query text in the SearchBar. /// Gets a string containing the query text in the SearchBar.

Просмотреть файл

@ -1,12 +1,21 @@
using AndroidX.AppCompat.Widget; using System.Linq;
using Android.Widget;
using SearchView = AndroidX.AppCompat.Widget.SearchView;
namespace Microsoft.Maui.Handlers namespace Microsoft.Maui.Handlers
{ {
public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, SearchView> public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, SearchView>
{ {
EditText? _editText;
public EditText? QueryEditor => _editText;
protected override SearchView CreateNativeView() protected override SearchView CreateNativeView()
{ {
return new SearchView(Context); var searchView = new SearchView(Context);
_editText = searchView.GetChildrenOfType<EditText>().First();
return searchView;
} }
public static void MapText(SearchBarHandler handler, ISearchBar searchBar) public static void MapText(SearchBarHandler handler, ISearchBar searchBar)
@ -18,5 +27,10 @@ namespace Microsoft.Maui.Handlers
{ {
handler.TypedNativeView?.UpdatePlaceholder(searchBar); handler.TypedNativeView?.UpdatePlaceholder(searchBar);
} }
public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar)
{
handler.QueryEditor?.UpdateHorizontalTextAlignment(searchBar);
}
} }
} }

Просмотреть файл

@ -8,5 +8,6 @@ namespace Microsoft.Maui.Handlers
public static void MapText(IViewHandler handler, ISearchBar searchBar) { } public static void MapText(IViewHandler handler, ISearchBar searchBar) { }
public static void MapPlaceholder(IViewHandler handler, ISearchBar searchBar) { } public static void MapPlaceholder(IViewHandler handler, ISearchBar searchBar) { }
public static void MapHorizontalTextAlignment(IViewHandler handler, ISearchBar searchBar) { }
} }
} }

Просмотреть файл

@ -5,7 +5,8 @@
public static PropertyMapper<ISearchBar, SearchBarHandler> SearchBarMapper = new PropertyMapper<ISearchBar, SearchBarHandler>(ViewHandler.ViewMapper) public static PropertyMapper<ISearchBar, SearchBarHandler> SearchBarMapper = new PropertyMapper<ISearchBar, SearchBarHandler>(ViewHandler.ViewMapper)
{ {
[nameof(ISearchBar.Text)] = MapText, [nameof(ISearchBar.Text)] = MapText,
[nameof(ISearchBar.Placeholder)] = MapPlaceholder [nameof(ISearchBar.Placeholder)] = MapPlaceholder,
[nameof(ISearchBar.HorizontalTextAlignment)] = MapHorizontalTextAlignment
}; };
public SearchBarHandler() : base(SearchBarMapper) public SearchBarHandler() : base(SearchBarMapper)

Просмотреть файл

@ -1,11 +1,19 @@
using System;
using UIKit; using UIKit;
namespace Microsoft.Maui.Handlers namespace Microsoft.Maui.Handlers
{ {
public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, UISearchBar> public partial class SearchBarHandler : AbstractViewHandler<ISearchBar, UISearchBar>
{ {
protected override UISearchBar CreateNativeView() => new UISearchBar(); UITextField? _textField;
protected override UISearchBar CreateNativeView()
{
var searchBar = new UISearchBar();
_textField = searchBar.FindDescendantView<UITextField>();
return searchBar;
}
public static void MapText(SearchBarHandler handler, ISearchBar searchBar) public static void MapText(SearchBarHandler handler, ISearchBar searchBar)
{ {
@ -16,5 +24,10 @@ namespace Microsoft.Maui.Handlers
{ {
handler.TypedNativeView?.UpdatePlaceholder(searchBar); handler.TypedNativeView?.UpdatePlaceholder(searchBar);
} }
public static void MapHorizontalTextAlignment(SearchBarHandler handler, ISearchBar searchBar)
{
handler.TypedNativeView?.UpdateHorizontalTextAlignment(searchBar, handler._textField);
}
} }
} }

Просмотреть файл

@ -1,4 +1,4 @@
using AndroidX.AppCompat.Widget; using SearchView = AndroidX.AppCompat.Widget.SearchView;
namespace Microsoft.Maui namespace Microsoft.Maui
{ {

Просмотреть файл

@ -5,7 +5,7 @@ using Android.Widget;
namespace Microsoft.Maui namespace Microsoft.Maui
{ {
public static class LabelExtensions public static class TextViewExtensions
{ {
public static void UpdateText(this TextView textView, ILabel label) public static void UpdateText(this TextView textView, ILabel label)
{ {
@ -40,9 +40,20 @@ namespace Microsoft.Maui
textView.SetTextSize(ComplexUnitType.Sp, sp); textView.SetTextSize(ComplexUnitType.Sp, sp);
} }
public static void UpdateHorizontalTextAlignment(this TextView textView, ILabel label) public static void UpdateHorizontalTextAlignment(this TextView textView, ITextAlignment text)
{ {
textView.Gravity = label.HorizontalTextAlignment.ToHorizontalGravityFlags(); if (textView.Context!.HasRtlSupport())
{
// We want to use TextAlignment where possible because it doesn't conflict with the
// overall gravity of the underlying control
textView.TextAlignment = text.HorizontalTextAlignment.ToTextAlignment();
}
else
{
// But if RTL support is not available for some reason, we have to resort
// to gravity, because Android will simply ignore text alignment
textView.Gravity = text.HorizontalTextAlignment.ToHorizontalGravityFlags();
}
} }
public static void UpdateLineBreakMode(this TextView textView, ILabel label) public static void UpdateLineBreakMode(this TextView textView, ILabel label)

Просмотреть файл

@ -0,0 +1,28 @@
using System.Collections.Generic;
using AView = Android.Views.View;
using AViewGroup = Android.Views.ViewGroup;
namespace Microsoft.Maui
{
public static class ViewGroupExtensions
{
public static IEnumerable<T> GetChildrenOfType<T>(this AViewGroup self) where T : AView
{
for (var i = 0; i < self.ChildCount; i++)
{
AView? child = self.GetChildAt(i);
if (child is T typedChild)
yield return typedChild;
if (child is AViewGroup)
{
IEnumerable<T>? myChildren = (child as AViewGroup)?.GetChildrenOfType<T>();
if (myChildren != null)
foreach (T nextChild in myChildren)
yield return nextChild;
}
}
}
}
}

Просмотреть файл

@ -13,5 +13,23 @@ namespace Microsoft.Maui
{ {
uiSearchBar.Placeholder = searchBar.Placeholder; 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);
}
} }
} }

Просмотреть файл

@ -1,10 +1,41 @@
using Microsoft.Maui.Handlers; using System.Linq;
using System.Threading.Tasks;
using Android.Widget;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Handlers;
using Xunit;
using SearchView = AndroidX.AppCompat.Widget.SearchView; using SearchView = AndroidX.AppCompat.Widget.SearchView;
namespace Microsoft.Maui.DeviceTests namespace Microsoft.Maui.DeviceTests
{ {
public partial class SearchBarHandlerTests public partial class SearchBarHandlerTests
{ {
[Fact(DisplayName = "Horizontal TextAlignment Initializes Correctly")]
public async Task HorizontalTextAlignmentInitializesCorrectly()
{
var xplatHorizontalTextAlignment = TextAlignment.End;
var searchBarStub = new SearchBarStub()
{
Text = "Test",
HorizontalTextAlignment = xplatHorizontalTextAlignment
};
Android.Views.TextAlignment expectedValue = Android.Views.TextAlignment.ViewEnd;
var values = await GetValueAsync(searchBarStub, (handler) =>
{
return new
{
ViewValue = searchBarStub.HorizontalTextAlignment,
NativeViewValue = GetNativeTextAlignment(handler)
};
});
Assert.Equal(xplatHorizontalTextAlignment, values.ViewValue);
values.NativeViewValue.AssertHasFlag(expectedValue);
}
SearchView GetNativeSearchBar(SearchBarHandler searchBarHandler) => SearchView GetNativeSearchBar(SearchBarHandler searchBarHandler) =>
(SearchView)searchBarHandler.View; (SearchView)searchBarHandler.View;
@ -13,5 +44,16 @@ namespace Microsoft.Maui.DeviceTests
string GetNativePlaceholder(SearchBarHandler searchBarHandler) => string GetNativePlaceholder(SearchBarHandler searchBarHandler) =>
GetNativeSearchBar(searchBarHandler).QueryHint; GetNativeSearchBar(searchBarHandler).QueryHint;
Android.Views.TextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHandler)
{
var searchView = GetNativeSearchBar(searchBarHandler);
var editText = searchView.GetChildrenOfType<EditText>().FirstOrDefault();
if (editText == null)
return Android.Views.TextAlignment.Inherit;
return editText.TextAlignment;
}
} }
} }

Просмотреть файл

@ -1,17 +1,57 @@
using Microsoft.Maui.Handlers; using System.Threading.Tasks;
using Microsoft.Maui.DeviceTests.Stubs;
using Microsoft.Maui.Handlers;
using UIKit; using UIKit;
using Xunit;
namespace Microsoft.Maui.DeviceTests namespace Microsoft.Maui.DeviceTests
{ {
public partial class SearchBarHandlerTests public partial class SearchBarHandlerTests
{ {
UISearchBar GetNativeEntry(SearchBarHandler searchBarHandler) => [Fact(DisplayName = "Horizontal TextAlignment Updates Correctly")]
public async Task HorizontalTextAlignmentInitializesCorrectly()
{
var xplatHorizontalTextAlignment = TextAlignment.End;
var searchBarStub = new SearchBarStub()
{
Text = "Test",
HorizontalTextAlignment = xplatHorizontalTextAlignment
};
UITextAlignment expectedValue = UITextAlignment.Right;
var values = await GetValueAsync(searchBarStub, (handler) =>
{
return new
{
ViewValue = searchBarStub.HorizontalTextAlignment,
NativeViewValue = GetNativeTextAlignment(handler)
};
});
Assert.Equal(xplatHorizontalTextAlignment, values.ViewValue);
values.NativeViewValue.AssertHasFlag(expectedValue);
}
UISearchBar GetNativeSearchBar(SearchBarHandler searchBarHandler) =>
(UISearchBar)searchBarHandler.View; (UISearchBar)searchBarHandler.View;
string GetNativeText(SearchBarHandler searchBarHandler) => string GetNativeText(SearchBarHandler searchBarHandler) =>
GetNativeEntry(searchBarHandler).Text; GetNativeSearchBar(searchBarHandler).Text;
string GetNativePlaceholder(SearchBarHandler searchBarHandler) => string GetNativePlaceholder(SearchBarHandler searchBarHandler) =>
GetNativeEntry(searchBarHandler).Placeholder; GetNativeSearchBar(searchBarHandler).Placeholder;
UITextAlignment GetNativeTextAlignment(SearchBarHandler searchBarHandler)
{
var uiSearchBar = GetNativeSearchBar(searchBarHandler);
var textField = uiSearchBar.FindDescendantView<UITextField>();
if (textField == null)
return UITextAlignment.Left;
return textField.TextAlignment;
}
} }
} }

Просмотреть файл

@ -7,5 +7,7 @@
public string Text { get => _text; set => SetProperty(ref _text, value); } public string Text { get => _text; set => SetProperty(ref _text, value); }
public string Placeholder { get; set; } public string Placeholder { get; set; }
public TextAlignment HorizontalTextAlignment { get; set; }
} }
} }