* [Shell,iOS] Add Colors, Font , HorizontalAlignment and keyboard support to SearchHandler
* {Android]Allow set colors and customize text on Shell SearchHandler
* [Android] Update icons color on SearchView
* [Android] Update Placeholdericon color from one place
* [iOS] Update shell cancel button colours
* [iOS,Android] Update IconPlaceholder color
* [Core] Fix missing region
* [Controls] Add shell SearchHandlerPage
* [iOS] Fix alignment changes on SearchHandlerAppearanceTraccker
* [Shell,Core] Add focus to SearchHandler
* [Android] Add SearchHandler focus
* [iOS] Add SearchHandler focus
* [Controls] Reuse dynamic gallery for SearchHandler
* [Controls] Fixes to gallery
* [Android] Remove cancel button code
* [Android] Add extension point for SearchHandlerAppearanceTracker
* [Android] Fix spelling
* fix bad merge
fixes #3125
fixes #5768
Label _titleLabel;
Slider[] _sliders;
- BoxView _box;
+ Frame _box;
Label _hexLabel;
Switch _useDefault;
grid.AddChild(_sliders[i], 1, i + 1);
}
- _box = new BoxView
+ _box = new Frame
{
- Color = Color,
+ BackgroundColor = Color,
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
+ BorderColor = Color.Black,
+
};
grid.AddChild(_box, 2, 1, 1, 3);
Content = grid;
}
+ public void InitWithColor(Color color)
+ {
+ _sliders[0].Value = color.R * 255;
+ _sliders[1].Value = color.G * 255;
+ _sliders[2].Value = color.B * 255;
+ _sliders[3].Value = color.A * 255;
+ }
+
public string Title
{
get => (string)GetValue(TitleProperty);
{
var color = picker.UseDefault ? Color.Default : picker.Color;
picker._hexLabel.Text = color.IsDefault ? "<default>" : ColorToHex(color);
- picker._box.Color = color;
+ picker._box.BackgroundColor = color;
picker.ColorPicked?.Invoke(picker, new ColorPickedEventArgs(color));
}
}
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
+using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Controls
{
+ [Preserve(AllMembers = true)]
public class DynamicViewGallery : ContentPage
{
- Dictionary<string, (Func<View> ctor, NamedAction[] methods)> _testedTypes;
-
- HashSet<string> _exceptProperties = new HashSet<string>
- {
- AutomationIdProperty.PropertyName,
- ClassIdProperty.PropertyName,
- "StyleId",
- };
-
- View _element;
-
- StackLayout _propertyLayout;
-
- StackLayout _pageContent;
-
- Picker _selector;
-
- public DynamicViewGallery()
- {
- _testedTypes = new Dictionary<string, (Func<View> ctor, NamedAction[] methods)>
+ internal static Dictionary<string, (Func<object> ctor, NamedAction[] methods)> TestedTypes = new Dictionary<string, (Func<object> ctor, NamedAction[] methods)>
{
{ nameof(ActivityIndicator), (() => new ActivityIndicator() { IsRunning = false }, null) },
{ nameof(ProgressBar), (() => new ProgressBar(), null) },
{ nameof(TimePicker), (() => new TimePicker(), null) },
{ nameof(ListView), (() => new ListView(), null) },
{ nameof(BoxView), (() => new BoxView(), null) },
+
};
+ internal static HashSet<string> ExceptProperties = new HashSet<string>
+ {
+ AutomationIdProperty.PropertyName,
+ ClassIdProperty.PropertyName,
+ "StyleId",
+ };
+
+ View _element;
+
+ StackLayout _propertyLayout;
+
+ StackLayout _pageContent;
+
+ Picker _selector;
+
+ public DynamicViewGallery()
+ {
_selector = new Picker();
- foreach (var item in _testedTypes)
+ foreach (var item in TestedTypes)
_selector.Items.Add(item.Key.ToString());
_selector.SelectedIndexChanged += TypeSelected;
var elementType = _element.GetType();
+ GetProperties(_element, elementType, _propertyLayout);
+
+ _pageContent.Children.Add(_element);
+ }
+
+ internal static void GetProperties(BindableObject element, Type elementType, StackLayout propertyLayout)
+ {
var publicProperties = elementType
- .GetProperties(BindingFlags.Public | BindingFlags.Instance)
- .Where(p => p.CanRead && p.CanWrite && !_exceptProperties.Contains(p.Name));
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead && p.CanWrite && !ExceptProperties.Contains(p.Name));
// BindableProperty used to clean property values
var bindableProperties = elementType
.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(p => p.FieldType.IsAssignableFrom(typeof(BindableProperty)))
- .Select(p => (BindableProperty)p.GetValue(_element));
+ .Select(p => (BindableProperty)p.GetValue(element));
foreach (var property in publicProperties)
{
var colorPicker = new ColorPicker
{
Title = property.Name,
- Color = (Color)property.GetValue(_element)
+ Color = (Color)property.GetValue(element)
};
- colorPicker.ColorPicked += (_, e) => property.SetValue(_element, e.Color);
- _propertyLayout.Children.Add(colorPicker);
+ colorPicker.ColorPicked += (_, e) => property.SetValue(element, e.Color);
+ propertyLayout.Children.Add(colorPicker);
}
else if (property.PropertyType == typeof(string))
{
- _propertyLayout.Children.Add(CreateStringPicker(property));
+ propertyLayout.Children.Add(CreateStringPicker(property, element));
}
else if (property.PropertyType == typeof(double) ||
property.PropertyType == typeof(float) ||
property.PropertyType == typeof(int))
{
- _propertyLayout.Children.Add(
- CreateValuePicker(property, bindableProperties.FirstOrDefault(p => p.PropertyName == property.Name)));
+ propertyLayout.Children.Add(
+ CreateValuePicker(property, bindableProperties.FirstOrDefault(p => p.PropertyName == property.Name), element));
}
else if (property.PropertyType == typeof(bool))
{
- _propertyLayout.Children.Add(CreateBooleanPicker(property));
+ propertyLayout.Children.Add(CreateBooleanPicker(property, element));
}
else if (property.PropertyType == typeof(Thickness))
{
- _propertyLayout.Children.Add(CreateThicknessPicker(property));
+ propertyLayout.Children.Add(CreateThicknessPicker(property, element));
+ }
+ else if (property.PropertyType == typeof(TextAlignment))
+ {
+ propertyLayout.Children.Add(CreateEnumPicker(property, element, typeof(TextAlignment)));
}
else
{
}
}
- var customMethods = _testedTypes[elementType.Name].methods;
+ var customMethods = TestedTypes[elementType.Name].methods;
if (customMethods != null)
{
- _propertyLayout.Children.Add(new Label {
+ propertyLayout.Children.Add(new Label
+ {
Text = "Custom methods",
FontSize = 20,
Margin = 6
foreach (var method in customMethods)
{
- _propertyLayout.Children.Add(new Button
+ propertyLayout.Children.Add(new Button
{
Text = method.Name,
FontAttributes = FontAttributes.Bold,
Padding = 6,
- Command = new Command(() => method.Action(_element))
+ Command = new Command(() => method.Action(element))
});
}
}
-
- _pageContent.Children.Add(_element);
}
void TypeSelected(object sender, EventArgs e)
var oldElement = _element;
try
{
- _element = _testedTypes[(string)_selector.SelectedItem].ctor();
+ _element = TestedTypes[(string)_selector.SelectedItem].ctor() as View;
}
catch
{
OnElementUpdated(oldElement);
}
- Dictionary<string, (double min, double max)> _minMaxProperties = new Dictionary<string, (double min, double max)>
+ static Dictionary<string, (double min, double max)> _minMaxProperties = new Dictionary<string, (double min, double max)>
{
{ ScaleProperty.PropertyName, (0d, 1d) },
{ ScaleXProperty.PropertyName, (0d, 1d) },
{ PaddingElement.PaddingProperty.PropertyName, (-100, 100) },
};
- Grid CreateValuePicker(PropertyInfo property, BindableProperty bindableProperty)
+ static Grid CreateValuePicker(PropertyInfo property, BindableProperty bindableProperty, BindableObject element)
{
var min = 0d;
var max = 100d;
}
var isInt = property.PropertyType == typeof(int);
- var value = isInt ? (int)property.GetValue(_element) : (double)property.GetValue(_element);
+ var value = isInt ? (int)property.GetValue(element) : (double)property.GetValue(element);
var slider = new Slider(min, max, value);
var actions = new Grid
HeightRequest = 28,
Margin = 0,
Padding = 0,
- Command = new Command(() => _element.ClearValue(bindableProperty))
+ Command = new Command(() => element.ClearValue(bindableProperty))
}, 1, 0);
}
slider.ValueChanged += (_, e) =>
{
if (isInt)
- property.SetValue(_element, (int)e.NewValue);
+ property.SetValue(element, (int)e.NewValue);
else
- property.SetValue(_element, e.NewValue);
+ property.SetValue(element, e.NewValue);
valueLabel.Text = e.NewValue.ToString(isInt ? "0" : "0.#");
};
return actions;
}
- Grid CreateThicknessPicker(PropertyInfo property)
+ static Grid CreateEnumPicker(PropertyInfo property, BindableObject element, Type elementType)
+ {
+ var grid = new Grid
+ {
+ Padding = 0,
+ RowSpacing = 3,
+ ColumnSpacing = 3,
+ ColumnDefinitions =
+ {
+ new ColumnDefinition { Width = 100 },
+ new ColumnDefinition { Width = GridLength.Star }
+ },
+ };
+ grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0);
+ var picker = new Picker { Title = property.Name };
+ foreach (var item in Enum.GetNames(elementType))
+ picker.Items.Add(item);
+
+ picker.SelectedItem = property.GetValue(element).ToString();
+ grid.AddChild(picker, 1, 0);
+ picker.SelectedIndexChanged += (_, e) =>
+ {
+ var newEnumValue = Enum.Parse(elementType, picker.SelectedItem.ToString());
+ property.SetValue(element, newEnumValue);
+ };
+ element.PropertyChanged += (_, e) =>
+ {
+ if (e.PropertyName == property.Name)
+ {
+ var newVal = property.GetValue(element);
+ if (newVal.ToString() != picker.SelectedItem.ToString())
+ picker.SelectedItem = newVal;
+ }
+ };
+ return grid;
+ }
+
+ static Grid CreateThicknessPicker(PropertyInfo property, BindableObject element)
{
var grid = new Grid
{
};
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0, 2);
- var val = (Thickness)property.GetValue(_element);
+ var val = (Thickness)property.GetValue(element);
var sliders = new Slider[4];
var valueLabels = new Label[4];
for (int i = 0; i < 4; i++)
void ThicknessChanged(object sender, ValueChangedEventArgs e)
{
- property.SetValue(_element, new Thickness(sliders[0].Value, sliders[1].Value, sliders[2].Value, sliders[3].Value));
+ property.SetValue(element, new Thickness(sliders[0].Value, sliders[1].Value, sliders[2].Value, sliders[3].Value));
for (int i = 0; i < valueLabels.Length; i++)
valueLabels[i].Text = sliders[i].Value.ToString("0");
}
return grid;
}
- Grid CreateBooleanPicker(PropertyInfo property)
+ static Grid CreateBooleanPicker(PropertyInfo property, BindableObject element)
{
var grid = new Grid
{
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0);
var boolSwitch = new Switch
{
- IsToggled = (bool)property.GetValue(_element),
+ IsToggled = (bool)property.GetValue(element),
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
- boolSwitch.Toggled += (_, e) => property.SetValue(_element, e.Value);
+ boolSwitch.Toggled += (_, e) => property.SetValue(element, e.Value);
grid.AddChild(boolSwitch, 1, 0);
- _element.PropertyChanged += (_, e) =>
+ element.PropertyChanged += (_, e) =>
{
if (e.PropertyName == property.Name)
{
- var newVal = (bool)property.GetValue(_element);
+ var newVal = (bool)property.GetValue(element);
if (newVal != boolSwitch.IsToggled)
boolSwitch.IsToggled = newVal;
}
return grid;
}
- Grid CreateStringPicker(PropertyInfo property)
+ static Grid CreateStringPicker(PropertyInfo property, BindableObject element)
{
var grid = new Grid
{
grid.AddChild(new Label { Text = property.Name, FontAttributes = FontAttributes.Bold }, 0, 0);
var entry = new Entry
{
- Text = (string)property.GetValue(_element),
+ Text = (string)property.GetValue(element),
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand
};
- entry.TextChanged += (_, e) => property.SetValue(_element, e.NewTextValue);
+ entry.TextChanged += (_, e) => property.SetValue(element, e.NewTextValue);
grid.AddChild(entry, 0, 1);
- _element.PropertyChanged += (_, e) =>
+ element.PropertyChanged += (_, e) =>
{
if (e.PropertyName == property.Name)
{
- var newVal = (string)property.GetValue(_element);
+ var newVal = (string)property.GetValue(element);
if (newVal != entry.Text)
entry.Text = newVal;
}
return grid;
}
- class NamedAction
+ internal class NamedAction
{
public string Name { get; set; }
- public Action<View> Action { get; set; }
+ public Action<object> Action { get; set; }
}
- (Func<View> ctor, NamedAction[] methods) GetPicker()
+ static (Func<View> ctor, NamedAction[] methods) GetPicker()
{
return (ctor: () =>
{
<Shell.FlyoutHeader>
<local:FlyoutHeader />
</Shell.FlyoutHeader>
-
+
+ <ShellContent Title="Search" Route="search" ContentTemplate="{DataTemplate local:SearchHandlerPage}" />
<ShellSection FlyoutDisplayOptions="AsMultipleItems" Route="apps" Title="My apps & games" Icon="grid.png" Style="{StaticResource GreenShell}">
<ShellContent Route="updates" Title="Updates" ContentTemplate="{DataTemplate local:UpdatesPage}" />
<ShellContent Route="installed" Title="Installed" ContentTemplate="{DataTemplate local:InstalledPage}" />
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Xamarin.Forms;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration;
+using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
+using static Xamarin.Forms.Controls.DynamicViewGallery;
+using static Xamarin.Forms.Controls.XamStore.BasePage;
+
+namespace Xamarin.Forms.Controls.XamStore
+{
+ [Preserve(AllMembers = true)]
+ public class SearchHandlerPage : ContentPage
+ {
+ SearchHandler _searchHandler;
+ StackLayout _propertyLayout;
+ const string SearchHandlerKey = nameof(SearchHandler);
+
+ public SearchHandlerPage()
+ {
+ On<iOS>().SetUseSafeArea(true);
+
+ var searchHandlerKey = nameof(SearchHandler);
+
+ TestedTypes.Add(SearchHandlerKey, (AddSearchHandler, new NamedAction[] { new NamedAction { Name = nameof(Focus), Action = FocusUnfocusSearchHandler } }));
+
+ _searchHandler = TestedTypes[SearchHandlerKey].ctor() as SearchHandler;
+
+ _propertyLayout = new StackLayout
+ {
+ Spacing = 10,
+ Padding = 10,
+ Children = { new Button { Text = "Show/Hide SearchHandler", Command = new Command(()=> ShowHideSearchHandler()) }}
+ };
+ Content = new StackLayout
+ {
+ Children = {
+ new Label { Text = "Test SearchHandler on IOs that this text appears", HorizontalTextAlignment = TextAlignment.Center },
+ new ScrollView { Content = _propertyLayout }
+ }
+ };
+
+ GetProperties(_searchHandler, typeof(SearchHandler), _propertyLayout);
+
+ }
+
+ void FocusUnfocusSearchHandler(object searchHandler)
+ {
+ var sh = searchHandler as SearchHandler;
+ if (sh == null)
+ return;
+
+ if (sh.IsFocused)
+ sh.Unfocus();
+ else
+ sh.Focus();
+ }
+
+ void ShowHideSearchHandler()
+ {
+ if (_searchHandler == null)
+ {
+ _searchHandler = TestedTypes[SearchHandlerKey].ctor() as SearchHandler;
+ }
+ else
+ {
+ _searchHandler = null;
+
+ Shell.SetSearchHandler(this, _searchHandler);
+ }
+ }
+
+ internal CustomSearchHandler AddSearchHandler()
+ {
+ var searchHandler = new CustomSearchHandler();
+
+ searchHandler.BackgroundColor = Color.Orange;
+ searchHandler.CancelButtonColor = Color.Pink;
+ searchHandler.TextColor = Color.White;
+ searchHandler.PlaceholderColor = Color.Yellow;
+ searchHandler.HorizontalTextAlignment = TextAlignment.Center;
+ searchHandler.ShowsResults = true;
+
+ searchHandler.Keyboard = Keyboard.Numeric;
+
+ searchHandler.FontFamily = "ChalkboardSE-Regular";
+ searchHandler.FontAttributes = FontAttributes.Bold;
+
+ searchHandler.ClearIconName = "Clear";
+ searchHandler.ClearIconHelpText = "Clears the search field text";
+
+ searchHandler.ClearPlaceholderName = "Voice Search";
+ searchHandler.ClearPlaceholderHelpText = "Start voice search";
+
+ searchHandler.QueryIconName = "Search";
+ searchHandler.QueryIconHelpText = "Press to search app";
+
+ searchHandler.Placeholder = "Type to search";
+ searchHandler.ClearPlaceholderEnabled = true;
+ searchHandler.ClearPlaceholderIcon = "mic.png";
+
+ Shell.SetSearchHandler(this, searchHandler);
+ return searchHandler;
+ }
+
+ }
+}
+
{
var searchHandler = new CustomSearchHandler();
+ searchHandler.BackgroundColor = Color.Orange;
+ searchHandler.CancelButtonColor = Color.Pink;
+ searchHandler.TextColor = Color.White;
+ searchHandler.PlaceholderColor = Color.Yellow;
+ searchHandler.HorizontalTextAlignment = TextAlignment.Center;
searchHandler.ShowsResults = true;
+ searchHandler.Keyboard = Keyboard.Numeric;
+
+ searchHandler.FontFamily = "ChalkboardSE-Regular";
+ searchHandler.FontAttributes = FontAttributes.Bold;
+
searchHandler.ClearIconName = "Clear";
searchHandler.ClearIconHelpText = "Clears the search field text";
using System;
using System.Collections;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Windows.Input;
+using Xamarin.Forms.Internals;
+using static Xamarin.Forms.VisualElement;
namespace Xamarin.Forms
{
- public class SearchHandler : BindableObject, ISearchHandlerController
+ public class SearchHandler : BindableObject, ISearchHandlerController, IPlaceholderElement, IFontElement, ITextElement, ITextAlignmentElement
{
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static readonly BindablePropertyKey IsFocusedPropertyKey = BindableProperty.CreateReadOnly(nameof(IsFocused),
+ typeof(bool), typeof(VisualElement), default(bool), propertyChanged: OnIsFocusedPropertyChanged);
+
+ public event EventHandler<EventArgs> Focused;
+ public event EventHandler<EventArgs> Unfocused;
+
+ public static readonly BindableProperty IsFocusedProperty = IsFocusedPropertyKey.BindableProperty;
+
+ public bool IsFocused
+ {
+ get { return (bool)GetValue(IsFocusedProperty); }
+ }
+
+ static void OnIsFocusedPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ var element = (SearchHandler)bindable;
+
+ if (element == null)
+ {
+ return;
+ }
+
+ var isFocused = (bool)newvalue;
+ if (isFocused)
+ {
+ element.Focused?.Invoke(element, new EventArgs());
+ element.OnFocused();
+ }
+ else
+ {
+ element.Unfocused?.Invoke(element, new EventArgs());
+ element.OnUnfocus();
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void SetIsFocused(bool value)
+ {
+ SetValueCore(IsFocusedPropertyKey.BindableProperty, value);
+ }
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<FocusRequestArgs> FocusChangeRequested;
+
+ public bool Focus()
+ {
+ if (IsFocused)
+ return true;
+
+ if (FocusChangeRequested == null)
+ return false;
+
+ var arg = new FocusRequestArgs { Focus = true };
+ FocusChangeRequested(this, arg);
+ return arg.Result;
+ }
+
+ public void Unfocus()
+ {
+ if (!IsFocused)
+ return;
+
+ FocusChangeRequested?.Invoke(this, new FocusRequestArgs());
+ }
+
+ protected virtual void OnFocused()
+ {
+
+ }
+
+ protected virtual void OnUnfocus()
+ {
+
+ }
+
+ public static readonly BindableProperty KeyboardProperty = BindableProperty.Create(nameof(Keyboard), typeof(Keyboard), typeof(SearchHandler), Keyboard.Default, coerceValue: (o, v) => (Keyboard)v ?? Keyboard.Default);
+
+ public Keyboard Keyboard
+ {
+ get { return (Keyboard)GetValue(KeyboardProperty); }
+ set { SetValue(KeyboardProperty, value); }
+ }
+
+ public static readonly BindableProperty HorizontalTextAlignmentProperty = TextAlignmentElement.HorizontalTextAlignmentProperty;
+
+ void ITextAlignmentElement.OnHorizontalTextAlignmentPropertyChanged(TextAlignment oldValue, TextAlignment newValue)
+ {
+ }
+
+ public TextAlignment HorizontalTextAlignment
+ {
+ get { return (TextAlignment)GetValue(TextAlignmentElement.HorizontalTextAlignmentProperty); }
+ set { SetValue(TextAlignmentElement.HorizontalTextAlignmentProperty, value); }
+ }
+
+ public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
+
+ void ITextElement.OnTextColorPropertyChanged(Color oldValue, Color newValue)
+ {
+ }
+
+ public Color TextColor
+ {
+ get { return (Color)GetValue(TextElement.TextColorProperty); }
+ set { SetValue(TextElement.TextColorProperty, value); }
+ }
+
+ public static readonly BindableProperty CancelButtonColorProperty = BindableProperty.Create(nameof(CancelButtonColor), typeof(Color), typeof(SearchHandler), default(Color));
+
+ public static readonly BindableProperty FontFamilyProperty = FontElement.FontFamilyProperty;
+
+ public static readonly BindableProperty FontSizeProperty = FontElement.FontSizeProperty;
+
+ public static readonly BindableProperty FontAttributesProperty = FontElement.FontAttributesProperty;
+
+ public static readonly BindableProperty PlaceholderProperty = PlaceholderElement.PlaceholderProperty;
+
+ public static readonly BindableProperty PlaceholderColorProperty = PlaceholderElement.PlaceholderColorProperty;
+
+ public Color CancelButtonColor
+ {
+ get { return (Color)GetValue(CancelButtonColorProperty); }
+ set { SetValue(CancelButtonColorProperty, value); }
+ }
+
+
+ public FontAttributes FontAttributes
+ {
+ get { return (FontAttributes)GetValue(FontAttributesProperty); }
+ set { SetValue(FontAttributesProperty, value); }
+ }
+
+ public string FontFamily
+ {
+ get { return (string)GetValue(FontFamilyProperty); }
+ set { SetValue(FontFamilyProperty, value); }
+ }
+
+ [TypeConverter(typeof(FontSizeConverter))]
+ public double FontSize
+ {
+ get { return (double)GetValue(FontSizeProperty); }
+ set { SetValue(FontSizeProperty, value); }
+ }
+
+ void IFontElement.OnFontFamilyChanged(string oldValue, string newValue)
+ {
+ }
+
+ void IFontElement.OnFontSizeChanged(double oldValue, double newValue)
+ {
+ }
+
+ double IFontElement.FontSizeDefaultValueCreator() =>
+ Device.GetNamedSize(NamedSize.Default, typeof(SearchHandler));
+
+ void IFontElement.OnFontAttributesChanged(FontAttributes oldValue, FontAttributes newValue)
+ {
+ }
+
+ void IFontElement.OnFontChanged(Font oldValue, Font newValue)
+ {
+ }
+
+ public Color PlaceholderColor
+ {
+ get => (Color)GetValue(PlaceholderElement.PlaceholderColorProperty);
+ set => SetValue(PlaceholderElement.PlaceholderColorProperty, value);
+ }
+
+ public string Placeholder
+ {
+ get => (string)GetValue(PlaceholderElement.PlaceholderProperty);
+ set => SetValue(PlaceholderElement.PlaceholderProperty, value);
+ }
+
+ public static readonly BindableProperty BackgroundColorProperty = BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(SearchHandler), Color.Default);
+
+ public Color BackgroundColor
+ {
+ get { return (Color)GetValue(BackgroundColorProperty); }
+ set { SetValue(BackgroundColorProperty, value); }
+ }
+
event EventHandler<ListProxyChangedEventArgs> ISearchHandlerController.ListProxyChanged
{
add { _listProxyChanged += value; }
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(SearchHandler), null, BindingMode.OneTime);
- public static readonly BindableProperty PlaceholderProperty =
- BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(SearchHandler), null, BindingMode.OneTime);
-
public static readonly BindableProperty QueryIconHelpTextProperty =
BindableProperty.Create(nameof(QueryIconHelpText), typeof(string), typeof(SearchHandler), null, BindingMode.OneTime,
propertyChanged: (b, o, n) => ((SearchHandler)b).UpdateAutomationProperties());
set { SetValue(ItemTemplateProperty, value); }
}
- public string Placeholder
- {
- get { return (string)GetValue(PlaceholderProperty); }
- set { SetValue(PlaceholderProperty, value); }
- }
-
public string Query
{
get { return (string)GetValue(QueryProperty); }
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Android.App;
+using Android.Content;
+using Android.Graphics;
+using Android.OS;
+using Android.Runtime;
+using Android.Text;
+using Android.Util;
+using Android.Views;
+using Android.Widget;
+using AView = Android.Views.View;
+using AImageButton = Android.Widget.ImageButton;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.Android
+{
+ public class SearchHandlerAppearanceTracker : IDisposable
+ {
+ SearchHandler _searchHandler;
+ bool _disposed;
+ AView _control;
+ EditText _editText;
+ InputTypes _inputType;
+ TextColorSwitcher _textColorSwitcher;
+ TextColorSwitcher _hintColorSwitcher;
+
+ public SearchHandlerAppearanceTracker(IShellSearchView searchView)
+ {
+ _searchHandler = searchView.SearchHandler;
+ _control = searchView.View;
+ _searchHandler.PropertyChanged += SearchHandlerPropertyChanged;
+ _searchHandler.FocusChangeRequested += SearchHandlerFocusChangeRequested;
+ _editText = (_control as ViewGroup).GetChildrenOfType<EditText>().FirstOrDefault();
+ _textColorSwitcher = new TextColorSwitcher(_editText.TextColors, false);
+ _hintColorSwitcher = new TextColorSwitcher(_editText.HintTextColors, false);
+ UpdateSearchBarColors();
+ UpdateFont();
+ UpdateTextAlignment();
+ UpdateInputType();
+ }
+
+ protected virtual void SearchHandlerFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e)
+ {
+ e.Result = true;
+
+ if (e.Focus)
+ {
+ _control?.RequestFocus();
+ _control?.PostShowKeyboard();
+ }
+ else
+ {
+ _control.ClearFocus();
+ _control.HideKeyboard();
+ }
+ }
+
+ protected virtual void SearchHandlerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.Is(SearchHandler.BackgroundColorProperty))
+ {
+ UpdateBackgroundColor();
+ }
+ else if (e.Is(SearchHandler.TextColorProperty))
+ {
+ UpdateTextColor();
+ }
+ else if (e.IsOneOf(SearchHandler.PlaceholderColorProperty))
+ {
+ UpdatePlaceholderColor();
+ }
+ else if (e.IsOneOf(SearchHandler.FontFamilyProperty, SearchHandler.FontAttributesProperty, SearchHandler.FontSizeProperty))
+ {
+ UpdateFont();
+ }
+ else if (e.Is(SearchHandler.CancelButtonColorProperty))
+ {
+ UpdateCancelButtonColor();
+ }
+ else if (e.Is(SearchHandler.KeyboardProperty))
+ {
+ UpdateInputType();
+ }
+ else if (e.Is(SearchHandler.HorizontalTextAlignmentProperty))
+ {
+ UpdateTextAlignment();
+ }
+ }
+
+ void UpdateSearchBarColors()
+ {
+ UpdateBackgroundColor();
+ UpdateTextColor();
+ UpdatePlaceholderColor();
+ UpdateCancelButtonColor();
+ }
+
+ void UpdateFont()
+ {
+ _editText.Typeface = _searchHandler.ToTypeface();
+ _editText.SetTextSize(ComplexUnitType.Sp, (float)_searchHandler.FontSize);
+ }
+
+ void UpdatePlaceholderColor()
+ {
+ _hintColorSwitcher?.UpdateTextColor(_editText, _searchHandler.PlaceholderColor, _editText.SetHintTextColor);
+ }
+
+ void UpdateTextAlignment()
+ {
+ _editText.UpdateHorizontalAlignment(_searchHandler.HorizontalTextAlignment, _control.Context.HasRtlSupport(), Xamarin.Forms.TextAlignment.Center.ToVerticalGravityFlags());
+ }
+
+ void UpdateBackgroundColor()
+ {
+ var linearLayout = (_control as ViewGroup).GetChildrenOfType<LinearLayout>().FirstOrDefault();
+ linearLayout.SetBackgroundColor(_searchHandler.BackgroundColor.ToAndroid());
+ }
+
+ void UpdateCancelButtonColor()
+ {
+ //For now we are using the clear icon
+ //we should add a new cancel button that unfocus and hides keyboard
+ UpdateClearIconColor();
+ }
+
+ void UpdateClearIconColor()
+ {
+ UpdateImageButtonIconColor(nameof(SearchHandler.ClearIcon),_searchHandler.CancelButtonColor);
+ }
+
+ void UpdateClearPlaceholderIconColor()
+ {
+ UpdateImageButtonIconColor(nameof(SearchHandler.ClearPlaceholderIcon), _searchHandler.TextColor);
+ }
+
+ void UpdateTextColor()
+ {
+ var textColor = _searchHandler.TextColor;
+ _textColorSwitcher?.UpdateTextColor(_editText, textColor);
+ UpdateImageButtonIconColor("SearchIcon", textColor);
+ UpdateClearPlaceholderIconColor();
+ //we need to set the cursor to
+ }
+ void UpdateImageButtonIconColor(string tagName, Color toColor)
+ {
+ var image = _control.FindViewWithTag(tagName) as AImageButton;
+ if (image != null && image.Drawable != null)
+ {
+ if (!toColor.IsDefault)
+ image.Drawable.SetColorFilter(toColor.ToAndroid(), PorterDuff.Mode.SrcIn);
+ else
+ image.Drawable.ClearColorFilter();
+ }
+ }
+
+ void UpdateInputType()
+ {
+ var keyboard = _searchHandler.Keyboard;
+
+ _inputType = keyboard.ToInputType();
+ bool isSpellCheckEnableSet = false;
+ bool isSpellCheckEnable = false;
+ // model.IsSet(InputView.IsSpellCheckEnabledProperty)
+ if (!(keyboard is Internals.CustomKeyboard))
+ {
+ if (isSpellCheckEnableSet)
+ {
+ if ((_inputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)
+ {
+ if (!isSpellCheckEnable)
+ _inputType = _inputType | InputTypes.TextFlagNoSuggestions;
+ }
+ }
+ }
+ _editText.InputType = _inputType;
+
+ if (keyboard == Keyboard.Numeric)
+ {
+ _editText.KeyListener = GetDigitsKeyListener(_inputType);
+ }
+ }
+
+ protected virtual global::Android.Text.Method.NumberKeyListener GetDigitsKeyListener(InputTypes inputTypes)
+ {
+ // Override this in a custom renderer to use a different NumberKeyListener
+ // or to filter out input types you don't want to allow
+ // (e.g., inputTypes &= ~InputTypes.NumberFlagSigned to disallow the sign)
+ return LocalizedDigitsKeyListener.Create(inputTypes);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_searchHandler != null)
+ {
+ _searchHandler.FocusChangeRequested -= SearchHandlerFocusChangeRequested;
+ _searchHandler.PropertyChanged -= SearchHandlerPropertyChanged;
+ }
+ _searchHandler = null;
+ _control = null;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+ }
+}
\ No newline at end of file
using System;
using System.ComponentModel;
using System.Threading.Tasks;
-using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.Android.FastRenderers;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;
void IShellSearchView.LoadView()
{
LoadView(SearchHandler);
+ if (_searchHandlerAppearanceTracker == null)
+ _searchHandlerAppearanceTracker = CreateSearchHandlerAppearanceTracker();
+ }
+
+ protected virtual SearchHandlerAppearanceTracker CreateSearchHandlerAppearanceTracker()
+ {
+ return new SearchHandlerAppearanceTracker(this);
}
#endregion IShellSearchView
AImageButton _searchButton;
AppCompatAutoCompleteTextView _textBlock;
bool _disposed;
+ SearchHandlerAppearanceTracker _searchHandlerAppearanceTracker;
public ShellSearchView(Context context, IShellContext shellContext) : base(context)
{
{
_disposed = true;
+ _searchHandlerAppearanceTracker?.Dispose();
SearchHandler.PropertyChanged -= OnSearchHandlerPropertyChanged;
_textBlock.ItemClick -= OnTextBlockItemClicked;
_cardView = null;
_clearPlaceholderButton = null;
_shellContext = null;
+ _searchHandlerAppearanceTracker = null;
SearchHandler = null;
}
using (lp = new LayoutParams(LP.MatchParent, LP.MatchParent))
_cardView.LayoutParameters = lp;
-
var linearLayout = new LinearLayout(context);
using (lp = new LP(LP.MatchParent, LP.MatchParent))
linearLayout.LayoutParameters = lp;
int padding = (int)context.ToPixels(8);
- _searchButton = CreateImageButton(context, searchHandler, SearchHandler.QueryIconProperty, Resource.Drawable.abc_ic_search_api_material, padding, 0);
+ _searchButton = CreateImageButton(context, searchHandler, SearchHandler.QueryIconProperty, Resource.Drawable.abc_ic_search_api_material, padding, 0, "SearchIcon");
lp = new LinearLayout.LayoutParams(0, LP.MatchParent)
{
_textBlock.SetDropDownBackgroundDrawable(new ClipDrawableWrapper(_textBlock.DropDownBackground));
// A note on accessibility. The _textBlocks hint is what android defaults to reading in the screen
- // reader. Therefor we do not need to set something else.
+ // reader. Therefore, we do not need to set something else.
- _clearButton = CreateImageButton(context, searchHandler, SearchHandler.ClearIconProperty, Resource.Drawable.abc_ic_clear_material, 0, padding);
- _clearPlaceholderButton = CreateImageButton(context, searchHandler, SearchHandler.ClearPlaceholderIconProperty, -1, 0, padding);
+ _clearButton = CreateImageButton(context, searchHandler, SearchHandler.ClearIconProperty, Resource.Drawable.abc_ic_clear_material, 0, padding, nameof(SearchHandler.ClearIcon));
+ _clearPlaceholderButton = CreateImageButton(context, searchHandler, SearchHandler.ClearPlaceholderIconProperty, -1, 0, padding, nameof(SearchHandler.ClearPlaceholderIcon));
linearLayout.AddView(_searchButton);
linearLayout.AddView(_textBlock);
linearLayout.AddView(_clearButton);
linearLayout.AddView(_clearPlaceholderButton);
-
+
UpdateClearButtonState();
// hook all events down here to avoid getting events while doing setup
_clearButton.Click += OnClearButtonClicked;
_clearPlaceholderButton.Click += OnClearPlaceholderButtonClicked;
_searchButton.Click += OnSearchButtonClicked;
-
+
AddView(_cardView);
linearLayout.Dispose();
{
}
- AImageButton CreateImageButton(Context context, BindableObject bindable, BindableProperty property, int defaultImage, int leftMargin, int rightMargin)
+ AImageButton CreateImageButton(Context context, BindableObject bindable, BindableProperty property, int defaultImage, int leftMargin, int rightMargin, string tag)
{
var result = new AImageButton(context);
+ result.Tag = tag;
result.SetPadding(0, 0, 0, 0);
result.Focusable = false;
result.SetScaleType(ImageView.ScaleType.FitCenter);
{
base.Draw(canvas);
- // Step 1: Clip out the top shadow that was drawn as it wont look right when ligned up
+ // Step 1: Clip out the top shadow that was drawn as it wont look right when lined up
var paint = new Paint
{
Color = AColor.Black
<Compile Include="Renderers\MotionEventHelper.cs" />
<Compile Include="Renderers\PageContainer.cs" />
<Compile Include="Renderers\ScrollViewContainer.cs" />
+ <Compile Include="Renderers\SearchHandlerAppearanceTracker.cs" />
<Compile Include="Renderers\ShellBottomNavViewAppearanceTracker.cs" />
<Compile Include="Renderers\ShellItemRenderer.cs" />
<Compile Include="Renderers\ShellContentFragment.cs" />
return attributed;
}
- internal static NSAttributedString ToAttributed(this Span span, Element owner, Color defaultForegroundColor, TextAlignment textAlignment, double lineHeight = -1.0)
+ internal static NSAttributedString ToAttributed(this Span span, BindableObject owner, Color defaultForegroundColor, TextAlignment textAlignment, double lineHeight = -1.0)
{
if (span == null)
return null;
return attrString;
}
- internal static NSAttributedString ToAttributed(this FormattedString formattedString, Element owner,
+ internal static NSAttributedString ToAttributed(this FormattedString formattedString, BindableObject owner,
Color defaultForegroundColor, TextAlignment textAlignment = TextAlignment.Start, double lineHeight = -1.0)
{
if (formattedString == null)
--- /dev/null
+using System;
+using CoreGraphics;
+using Foundation;
+using UIKit;
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ public class SearchHandlerAppearanceTracker : IDisposable
+ {
+ UIColor _cancelButtonTextColorDefaultDisabled;
+ UIColor _cancelButtonTextColorDefaultHighlighted;
+ UIColor _cancelButtonTextColorDefaultNormal;
+ UIColor _defaultTextColor;
+ UIColor _defaultTintColor;
+ UIColor _defaultClearIconTintColor;
+ UIColor _defaultPlaceholderTintColor;
+ bool _hasCustomBackground;
+ UIColor _defaultBackgroundColor;
+ SearchHandler _searchHandler;
+ UISearchBar _uiSearchBar;
+ UIToolbar _numericAccessoryView;
+ bool _disposed;
+
+ public SearchHandlerAppearanceTracker(UISearchBar searchBar, SearchHandler searchHandler)
+ {
+ _searchHandler = searchHandler;
+ _searchHandler.PropertyChanged += SearchHandlerPropertyChanged;
+ _searchHandler.FocusChangeRequested += SearchHandlerFocusChangeRequested;
+ _uiSearchBar = searchBar;
+ _uiSearchBar.OnEditingStarted += OnEditingStarted;
+ _uiSearchBar.OnEditingStopped += OnEditingEnded;
+ _uiSearchBar.TextChanged += OnTextChanged;
+ _uiSearchBar.ShowsCancelButton = true;
+ GetDefaultSearchBarColors(_uiSearchBar);
+ var uiTextField = searchBar.FindDescendantView<UITextField>();
+ UpdateSearchBarColors();
+ UpdateSearchBarTextAlignment(uiTextField);
+ UpdateFont(uiTextField);
+ UpdateKeyboard();
+ }
+
+ public void UpdateSearchBarColors()
+ {
+ var cancelButton = _uiSearchBar.FindDescendantView<UIButton>();
+ var uiTextField = _uiSearchBar.FindDescendantView<UITextField>();
+
+ UpdateTextColor(uiTextField);
+ UpdateSearchBarPlaceholder(uiTextField);
+ UpdateCancelButtonColor(cancelButton);
+ UpdateSearchBarBackgroundColor(uiTextField);
+ }
+
+ void SearchHandlerFocusChangeRequested(object sender, VisualElement.FocusRequestArgs e)
+ {
+ if (e.Focus)
+ e.Result = _uiSearchBar.BecomeFirstResponder();
+ }
+
+ void SearchHandlerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.Is(SearchHandler.BackgroundColorProperty))
+ {
+ UpdateSearchBarBackgroundColor(_uiSearchBar.FindDescendantView<UITextField>());
+ }
+ else if (e.Is(SearchHandler.TextColorProperty))
+ {
+ UpdateTextColor(_uiSearchBar.FindDescendantView<UITextField>());
+ }
+ else if (e.IsOneOf(SearchHandler.PlaceholderColorProperty, SearchHandler.PlaceholderProperty))
+ {
+ UpdateSearchBarPlaceholder(_uiSearchBar.FindDescendantView<UITextField>());
+ }
+ else if (e.IsOneOf(SearchHandler.FontFamilyProperty, SearchHandler.FontAttributesProperty, SearchHandler.FontSizeProperty))
+ {
+ UpdateFont(_uiSearchBar.FindDescendantView<UITextField>());
+ }
+ else if (e.Is(SearchHandler.CancelButtonColorProperty))
+ {
+ UpdateCancelButtonColor(_uiSearchBar.FindDescendantView<UIButton>());
+ }
+ else if (e.Is(SearchHandler.KeyboardProperty))
+ {
+ UpdateKeyboard();
+ }
+ else if (e.Is(SearchHandler.HorizontalTextAlignmentProperty))
+ {
+ UpdateSearchBarTextAlignment(_uiSearchBar.FindDescendantView<UITextField>());
+ }
+ }
+
+ void GetDefaultSearchBarColors(UISearchBar searchBar)
+ {
+ _defaultTintColor = searchBar.BarTintColor;
+
+ var cancelButton = searchBar.FindDescendantView<UIButton>();
+ if (cancelButton != null)
+ {
+ _cancelButtonTextColorDefaultNormal = cancelButton.TitleColor(UIControlState.Normal);
+ _cancelButtonTextColorDefaultHighlighted = cancelButton.TitleColor(UIControlState.Highlighted);
+ _cancelButtonTextColorDefaultDisabled = cancelButton.TitleColor(UIControlState.Disabled);
+ }
+ }
+
+ void UpdateFont(UITextField textField)
+ {
+ if (textField == null)
+ return;
+
+ textField.Font = _searchHandler.ToUIFont();
+ }
+
+ void UpdateSearchBarBackgroundColor(UITextField textField)
+ {
+ if (textField == null)
+ return;
+
+ var backGroundColor = _searchHandler.BackgroundColor;
+
+ if (!_hasCustomBackground && backGroundColor.IsDefault)
+ return;
+
+ var backgroundView = textField.Subviews[0];
+
+ if (backGroundColor.IsDefault)
+ {
+ backgroundView.Layer.CornerRadius = 0;
+ backgroundView.ClipsToBounds = false;
+ backgroundView.BackgroundColor = _defaultBackgroundColor;
+ }
+
+ _hasCustomBackground = true;
+
+ backgroundView.Layer.CornerRadius = 10;
+ backgroundView.ClipsToBounds = true;
+ if (_defaultBackgroundColor == null)
+ _defaultBackgroundColor = backgroundView.BackgroundColor;
+ backgroundView.BackgroundColor = backGroundColor.ToUIColor();
+ }
+
+ void UpdateCancelButtonColor(UIButton cancelButton)
+ {
+ if (cancelButton == null)
+ return;
+
+ var cancelColor = _searchHandler.CancelButtonColor;
+ if (cancelColor.IsDefault)
+ {
+ cancelButton.SetTitleColor(_cancelButtonTextColorDefaultNormal, UIControlState.Normal);
+ cancelButton.SetTitleColor(_cancelButtonTextColorDefaultHighlighted, UIControlState.Highlighted);
+ cancelButton.SetTitleColor(_cancelButtonTextColorDefaultDisabled, UIControlState.Disabled);
+ }
+ else
+ {
+ var cancelUIColor = cancelColor.ToUIColor();
+ cancelButton.SetTitleColor(cancelUIColor, UIControlState.Normal);
+ cancelButton.SetTitleColor(cancelUIColor, UIControlState.Highlighted);
+ cancelButton.SetTitleColor(cancelUIColor, UIControlState.Disabled);
+ }
+
+ UpdateClearIconColor(cancelColor);
+ }
+
+ void UpdateSearchBarPlaceholder(UITextField textField)
+ {
+ if (textField == null)
+ return;
+
+ var formatted = (FormattedString)_searchHandler.Placeholder ?? string.Empty;
+ var targetColor = _searchHandler.PlaceholderColor;
+ var placeHolderColor = targetColor.IsDefault ? ColorExtensions.SeventyPercentGrey.ToColor() : targetColor;
+ textField.AttributedPlaceholder = formatted.ToAttributed(_searchHandler, placeHolderColor, _searchHandler.HorizontalTextAlignment);
+
+ //Center placeholder
+ //var width = (_uiSearchBar.Frame.Width / 2) - textField.AttributedPlaceholder.Size.Width;
+
+ //var paddingView = new UIImageView(new CGRect(0, 0, width, _uiSearchBar.Frame.Height));
+
+ //textField.LeftView = paddingView;
+ //var paddingView = UIView(frame: CGRect(x: 0, y: 0, width: width, height: searchBar.frame.height))
+
+ //searchBarTextField.leftView = paddingView
+
+ //searchBarTextField.leftViewMode = .unlessEditing
+ }
+
+ void UpdateTextColor(UITextField textField)
+ {
+ if (textField == null)
+ return;
+
+ _defaultTextColor = _defaultTextColor ?? textField.TextColor;
+ var targetColor = _searchHandler.TextColor;
+
+ textField.TextColor = targetColor.IsDefault ? _defaultTextColor : targetColor.ToUIColor();
+ UpdateSearchBarTintColor(targetColor);
+ UpdateSearchButtonIconColor(targetColor);
+ UpdateClearPlaceholderIconColor(targetColor);
+ }
+
+ void UpdateSearchBarTintColor(Color targetColor)
+ {
+ _uiSearchBar.TintColor = targetColor.IsDefault ? _defaultTintColor : targetColor.ToUIColor();
+ }
+
+ void UpdateSearchButtonIconColor(Color targetColor)
+ {
+ var imageView = _uiSearchBar.FindDescendantView<UITextField>()?.LeftView as UIImageView;
+
+ _defaultClearIconTintColor = _defaultClearIconTintColor ?? imageView.TintColor;
+
+ SetSearchBarIconColor(imageView, targetColor, _defaultClearIconTintColor);
+ }
+
+ void UpdateClearPlaceholderIconColor(Color targetColor)
+ {
+ var uiTextField = _uiSearchBar.FindDescendantView<UITextField>();
+
+ var uiButton = uiTextField.FindDescendantView<UIButton>();
+
+ if (uiButton == null)
+ return;
+
+ _defaultPlaceholderTintColor = _defaultPlaceholderTintColor ?? uiButton?.ImageView?.TintColor;
+
+
+ SetSearchBarIconColor(uiButton, targetColor, _defaultPlaceholderTintColor);
+ uiButton.TintColor = targetColor.IsDefault ? _defaultPlaceholderTintColor : targetColor.ToUIColor();
+ }
+
+ void UpdateClearIconColor(Color targetColor)
+ {
+ var uiTextField = _uiSearchBar.FindDescendantView<UITextField>();
+
+ var uiButton = uiTextField.ValueForKey(new NSString("clearButton")) as UIButton;
+
+ if (uiButton == null)
+ return;
+
+ _defaultClearIconTintColor = _defaultClearIconTintColor ?? uiButton?.TintColor;
+
+ SetSearchBarIconColor(uiButton, targetColor, _defaultClearIconTintColor);
+
+ }
+
+ void UpdateSearchBarTextAlignment(UITextField textField)
+ {
+ if (textField == null)
+ return;
+
+ textField.TextAlignment = _searchHandler.HorizontalTextAlignment.ToNativeTextAlignment(EffectiveFlowDirection.Explicit);
+ }
+
+ void UpdateKeyboard()
+ {
+ var keyboard = _searchHandler.Keyboard;
+ _uiSearchBar.ApplyKeyboard(keyboard);
+
+ // iPhone does not have an enter key on numeric keyboards
+ if (Device.Idiom == TargetIdiom.Phone && (keyboard == Keyboard.Numeric || keyboard == Keyboard.Telephone))
+ {
+ _numericAccessoryView = _numericAccessoryView ?? CreateNumericKeyboardAccessoryView();
+ _uiSearchBar.InputAccessoryView = _numericAccessoryView;
+ }
+ else
+ {
+ _uiSearchBar.InputAccessoryView = null;
+ }
+
+ _uiSearchBar.ReloadInputViews();
+ }
+
+ void OnEditingEnded(object sender, EventArgs e)
+ {
+ _searchHandler.SetIsFocused(false);
+ }
+
+ void OnEditingStarted(object sender, EventArgs e)
+ {
+ UpdateCancelButtonColor(_uiSearchBar.FindDescendantView<UIButton>());
+ _searchHandler.SetIsFocused(true);
+ //ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
+ }
+
+ void OnTextChanged(object sender, UISearchBarTextChangedEventArgs e)
+ {
+ UpdateCancelButtonColor(_uiSearchBar.FindDescendantView<UIButton>());
+ }
+
+
+ void OnSearchButtonClicked(object sender, EventArgs e)
+ {
+ ((ISearchHandlerController)_searchHandler).QueryConfirmed();
+ _uiSearchBar.ResignFirstResponder();
+ }
+
+ UIToolbar CreateNumericKeyboardAccessoryView()
+ {
+ var keyboardWidth = UIScreen.MainScreen.Bounds.Width;
+ var accessoryView = new UIToolbar(new CGRect(0, 0, keyboardWidth, 44)) { BarStyle = UIBarStyle.Default, Translucent = true };
+
+ var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
+ var searchButton = new UIBarButtonItem(UIBarButtonSystemItem.Search, OnSearchButtonClicked);
+ accessoryView.SetItems(new[] { spacer, searchButton }, false);
+
+ return accessoryView;
+ }
+
+ static void SetSearchBarIconColor(UIImageView imageView, Color targetColor, UIColor defaultTintColor)
+ {
+ var icon = imageView?.Image;
+
+ if (icon == null || (targetColor.IsDefault && defaultTintColor == null))
+ return;
+
+ var newIcon = icon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
+ imageView.TintColor = targetColor.IsDefault ? defaultTintColor : targetColor.ToUIColor();
+ imageView.Image = newIcon;
+ }
+
+ static void SetSearchBarIconColor(UIButton button, Color targetColor, UIColor defaultTintColor)
+ {
+ var icon = button.ImageView?.Image;
+
+ if (icon == null || (targetColor.IsDefault && defaultTintColor == null))
+ return;
+
+ var newIcon = icon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
+ button.SetImage(newIcon, UIControlState.Normal);
+ button.SetImage(newIcon, UIControlState.Selected);
+ button.SetImage(newIcon, UIControlState.Highlighted);
+ button.TintColor = button.ImageView.TintColor = targetColor.IsDefault ? defaultTintColor : targetColor.ToUIColor();
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_uiSearchBar != null)
+ {
+ _uiSearchBar.OnEditingStarted -= OnEditingStarted;
+ _uiSearchBar.OnEditingStopped -= OnEditingEnded;
+ _uiSearchBar.TextChanged -= OnTextChanged;
+ }
+ if (_searchHandler != null)
+ {
+ _searchHandler.FocusChangeRequested -= SearchHandlerFocusChangeRequested;
+ _searchHandler.PropertyChanged -= SearchHandlerPropertyChanged;
+ }
+ _searchHandler = null;
+ _uiSearchBar = null;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+ }
+}
SearchHandler _searchHandler;
Page _page;
NSCache _nSCache;
+ SearchHandlerAppearanceTracker _searchHandlerAppearanceTracker;
BackButtonBehavior BackButtonBehavior { get; set; }
UINavigationItem NavigationItem { get; set; }
{
if (oldPage != null)
{
+ oldPage.Appearing -= PageAppearing;
oldPage.PropertyChanged -= OnPagePropertyChanged;
((INotifyCollectionChanged)oldPage.ToolbarItems).CollectionChanged -= OnToolbarItemsChanged;
}
if (newPage != null)
{
+ newPage.Appearing += PageAppearing;
newPage.PropertyChanged += OnPagePropertyChanged;
((INotifyCollectionChanged)newPage.ToolbarItems).CollectionChanged += OnToolbarItemsChanged;
SetBackButtonBehavior(Shell.GetBackButtonBehavior(newPage));
void AttachSearchController()
{
+
if (SearchHandler.ShowsResults)
{
_resultsRenderer = _context.CreateShellSearchResultsRenderer();
}
searchBar.ShowsBookmarkButton = SearchHandler.ClearPlaceholderEnabled;
+
+ _searchHandlerAppearanceTracker = new SearchHandlerAppearanceTracker(searchBar, SearchHandler);
}
void BookmarkButtonClicked(object sender, EventArgs e)
void DettachSearchController()
{
+ _searchHandlerAppearanceTracker.Dispose();
+ _searchHandlerAppearanceTracker = null;
if (Forms.IsiOS11OrNewer)
{
RemoveSearchController(NavigationItem);
async void SetSearchBarIcon(UISearchBar searchBar, ImageSource source, UISearchBarIcon icon)
{
var result = await source.GetNativeImageAsync();
- searchBar.SetImageforSearchBarIcon(result, icon, UIControlState.Normal);
+ var newResult = result.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate);
+ searchBar.SetImageforSearchBarIcon(newResult, icon, UIControlState.Normal);
+ searchBar.SetImageforSearchBarIcon(newResult, icon, UIControlState.Highlighted);
+ searchBar.SetImageforSearchBarIcon(newResult, icon, UIControlState.Selected);
+ }
+
+ void PageAppearing(object sender, EventArgs e)
+ {
+ //UIKIt will try to override our colors when the SearchController is inside the NavigationBar
+ //Best way was to force them to be set again when page is Appearing / ViewDidLoad
+ _searchHandlerAppearanceTracker?.UpdateSearchBarColors();
}
#endregion SearchHandler
{
if (disposing)
{
+ _searchHandlerAppearanceTracker?.Dispose();
+ Page.Appearing -= PageAppearing;
Page.PropertyChanged -= OnPagePropertyChanged;
((INotifyCollectionChanged)Page.ToolbarItems).CollectionChanged -= OnToolbarItemsChanged;
((IShellController)_context.Shell).RemoveFlyoutBehaviorObserver(this);
SetBackButtonBehavior(null);
_rendererRef = null;
NavigationItem = null;
+ _searchHandlerAppearanceTracker = null;
_disposed = true;
}
}
#endregion IDisposable Support
}
-}
\ No newline at end of file
+}
<Compile Include="Extensions\FontExtensions.Shared.cs" />
<Compile Include="Renderers\ButtonLayoutManager.cs" />
<Compile Include="Renderers\IButtonLayoutRenderer.cs" />
+ <Compile Include="Renderers\SearchHandlerAppearanceTracker.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ar.resx" />
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=64B48C7709A839499C5E49DBCE502A37/@KeyIndexDefined">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File64B48C7709A839499C5E49DBCE502A37/@KeyIndexDefined">True</s:Boolean>
<s:Double x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=File64B48C7709A839499C5E49DBCE502A37/RelativePriority/@EntryValue">1</s:Double>
+ <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
+ <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>