Change SearchBar base class to InputView (#3248) fixes #2902
authorjcmanke <jmanke1227@gmail.com>
Fri, 7 Dec 2018 13:31:56 +0000 (07:31 -0600)
committerRui Marinho <me@ruimarinho.net>
Fri, 7 Dec 2018 13:31:56 +0000 (13:31 +0000)
* Changed SearchView base class from View to InputView

* Minor code cleanup

Xamarin.Forms.Controls/CoreGalleryPages/EditorCoreGalleryPage.cs
Xamarin.Forms.Controls/CoreGalleryPages/EntryCoreGalleryPage.cs
Xamarin.Forms.Controls/CoreGalleryPages/KeyboardCoreGalleryPage.cs
Xamarin.Forms.Controls/CoreGalleryPages/SearchBarCoreGalleryPage.cs
Xamarin.Forms.Core/SearchBar.cs
Xamarin.Forms.CustomAttributes/TestAttributes.cs
Xamarin.Forms.Platform.Android/Renderers/SearchBarRenderer.cs
Xamarin.Forms.Platform.UAP/SearchBarRenderer.cs
Xamarin.Forms.Platform.iOS/Extensions/Extensions.cs
Xamarin.Forms.Platform.iOS/Renderers/SearchBarRenderer.cs

index 880486c..b8ba817 100644 (file)
@@ -35,7 +35,10 @@ namespace Xamarin.Forms.Controls
                        var textColorDisabledContainer = new ViewContainer<Editor>(Test.Editor.TextColor,
                                new Editor { Text = "I should have the default disabled text color", TextColor = Color.Red, IsEnabled = false });
 
-                       var maxLengthContainer = new ViewContainer<Editor>(Test.Editor.MaxLength, new Editor { MaxLength = 3 });
+                       var keyboardContainer = new ViewContainer<Editor>(Test.InputView.Keyboard,
+                               new Editor { Keyboard = Keyboard.Numeric });
+
+                       var maxLengthContainer = new ViewContainer<Editor>(Test.InputView.MaxLength, new Editor { MaxLength = 3 });
 
                        Add(completedContainer);
                        Add(textContainer);
@@ -49,6 +52,7 @@ namespace Xamarin.Forms.Controls
                        Add(textFontSizeLargeContainer);
                        Add(textColorContainer);
                        Add(textColorDisabledContainer);
+                       Add(keyboardContainer);
                        Add(maxLengthContainer);
                }
        }
index 1ecd808..67bc5cd 100644 (file)
@@ -64,7 +64,7 @@ namespace Xamarin.Forms.Controls
                        var passwordColorContainer = new ViewContainer<Entry> (Test.Entry.PasswordColor,
                                new Entry { IsPassword = true, Text = "12345", TextColor = Color.Red });
 
-                       var maxLengthContainer = new ViewContainer<Entry>(Test.Entry.MaxLength, new Entry { MaxLength = 3 });
+                       var maxLengthContainer = new ViewContainer<Entry>(Test.InputView.MaxLength,     new Entry { MaxLength = 3 });
 
                        var isPasswordInputScopeContainer = new ViewContainer<Entry>(Test.Entry.IsPasswordNumeric,      new Entry { Keyboard = Keyboard.Numeric });
                        var switchPasswordButton = new Button
index 5511004..e29a6e6 100644 (file)
@@ -23,7 +23,7 @@ namespace Xamarin.Forms.Controls
                        var layout = new StackLayout ();
 
                        foreach (var keyboardType in keyboardTypes) {
-                               var viewContainer = new ViewContainer<Entry> (Test.Entry.Keyboard, new Entry { Placeholder = keyboardType.ToString (), Keyboard = keyboardType } );
+                               var viewContainer = new ViewContainer<Entry> (Test.InputView.Keyboard, new Entry { Placeholder = keyboardType.ToString (), Keyboard = keyboardType } );
                                layout.Children.Add (viewContainer.ContainerLayout);
                        }
 
@@ -40,7 +40,7 @@ namespace Xamarin.Forms.Controls
                        };
 
                        foreach (var customKeyboard in customKeyboards) {
-                               var viewContainer = new ViewContainer<Entry> (Test.Entry.Keyboard, new Entry { Placeholder = customKeyboard.Item1, Keyboard = customKeyboard.Item2 } );
+                               var viewContainer = new ViewContainer<Entry> (Test.InputView.Keyboard, new Entry { Placeholder = customKeyboard.Item1, Keyboard = customKeyboard.Item2 } );
                                layout.Children.Add (viewContainer.ContainerLayout);
                        }
 
index 0221b87..9a548cc 100644 (file)
@@ -64,6 +64,13 @@ namespace Xamarin.Forms.Controls
 
                        var placeholderColorContainer = new ViewContainer<SearchBar> (Test.SearchBar.PlaceholderColor,
                                new SearchBar { Placeholder = "I should be red", PlaceholderColor = Color.Red });
+
+                       var keyboardContainer = new ViewContainer<SearchBar>(Test.InputView.Keyboard,
+                               new SearchBar { Keyboard = Keyboard.Numeric });
+
+                       var maxLengthContainer = new ViewContainer<SearchBar>(Test.InputView.MaxLength, 
+                               new SearchBar { MaxLength = 3 });
+
                        Add (placeholderContainer);
                        Add (searchButtonPressedContainer);
                        Add (searchCommandContainer);
@@ -84,6 +91,8 @@ namespace Xamarin.Forms.Controls
                        Add (placeholderAlignmentEndContainer);
                        Add (textColorContainer);
                        Add (placeholderColorContainer);
+                       Add (keyboardContainer);
+                       Add (maxLengthContainer);
                }
        }
 }
\ No newline at end of file
index b233d53..2fe3eac 100644 (file)
@@ -7,7 +7,7 @@ using Xamarin.Forms.Platform;
 namespace Xamarin.Forms
 {
        [RenderWith(typeof(_SearchBarRenderer))]
-       public class SearchBar : View, IFontElement, IPlaceholderElement, ITextElement, ITextAlignmentElement, ISearchBarController, IElementConfiguration<SearchBar>
+       public class SearchBar : InputView, IFontElement, IPlaceholderElement, ITextElement, ITextAlignmentElement, ISearchBarController, IElementConfiguration<SearchBar>
        {
                public static readonly BindableProperty SearchCommandProperty = BindableProperty.Create("SearchCommand", typeof(ICommand), typeof(SearchBar), null, propertyChanged: OnCommandChanged);
 
index b5e0aef..6e26ac3 100644 (file)
@@ -494,6 +494,7 @@ namespace Xamarin.Forms.CustomAttributes
                public enum InputView
                {
                        Keyboard,
+                       MaxLength,
                }
 
                public enum Editor
@@ -506,7 +507,6 @@ namespace Xamarin.Forms.CustomAttributes
                        FontAttributes,
                        FontFamily,
                        FontSize,
-                       MaxLength
                }
 
                public enum Entry
@@ -517,7 +517,6 @@ namespace Xamarin.Forms.CustomAttributes
                        IsPassword,
                        Text,
                        TextColor,
-                       Keyboard,
                        HorizontalTextAlignmentStart,
                        HorizontalTextAlignmentCenter,
                        HorizontalTextAlignmentEnd,
@@ -531,7 +530,6 @@ namespace Xamarin.Forms.CustomAttributes
                        TextDisabledColor,
                        PlaceholderDisabledColor,
                        PasswordColor,
-                       MaxLength,
                        IsPasswordNumeric
                }
 
index 0be53eb..0336c68 100644 (file)
@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using Android.Content;
@@ -6,6 +7,7 @@ using Android.Content.Res;
 using Android.Graphics;
 using Android.OS;
 using Android.Text;
+using Android.Text.Method;
 using Android.Util;
 using Android.Widget;
 using AView = Android.Views.View;
@@ -74,16 +76,8 @@ namespace Xamarin.Forms.Platform.Android
 
                        }
 
-                       BuildVersionCodes androidVersion = Build.VERSION.SdkInt;
-                       if (androidVersion >= BuildVersionCodes.JellyBean)
-                               _inputType = searchView.InputType;
-                       else
-                       {
-                               // < API 16, Cannot get the default InputType for a SearchView
-                               _inputType = InputTypes.ClassText | InputTypes.TextFlagAutoComplete | InputTypes.TextFlagNoSuggestions;
-                       }
-
                        ClearFocus(searchView);
+                       UpdateInputType();
                        UpdatePlaceholder();
                        UpdateText();
                        UpdateEnabled();
@@ -92,6 +86,7 @@ namespace Xamarin.Forms.Platform.Android
                        UpdateAlignment();
                        UpdateTextColor();
                        UpdatePlaceholderColor();
+                       UpdateMaxLength();
 
                        if (e.OldElement == null)
                        {
@@ -126,6 +121,12 @@ namespace Xamarin.Forms.Platform.Android
                                UpdatePlaceholderColor();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateAlignment();
+                       else if (e.PropertyName == InputView.MaxLengthProperty.PropertyName)
+                               UpdateMaxLength();
+                       else if(e.PropertyName == InputView.KeyboardProperty.PropertyName)
+                               UpdateInputType();
+                       else if(e.PropertyName == InputView.IsSpellCheckEnabledProperty.PropertyName)
+                               UpdateInputType();
                }
 
                internal override void OnNativeFocusChanged(bool hasFocus)
@@ -223,5 +224,65 @@ namespace Xamarin.Forms.Platform.Android
                {
                        _textColorSwitcher?.UpdateTextColor(_editText, Element.TextColor);
                }
+
+               void UpdateMaxLength()
+               {
+                       _editText = _editText ?? Control.GetChildrenOfType<EditText>().FirstOrDefault();
+
+                       var currentFilters = new List<IInputFilter>(_editText?.GetFilters() ?? new IInputFilter[0]);
+
+                       for (var i = 0; i < currentFilters.Count; i++)
+                       {
+                               if (currentFilters[i] is InputFilterLengthFilter)
+                               {
+                                       currentFilters.RemoveAt(i);
+                                       break;
+                               }
+                       }
+
+                       currentFilters.Add(new InputFilterLengthFilter(Element.MaxLength));
+
+                       _editText?.SetFilters(currentFilters.ToArray());
+
+                       var currentControlText = Control.Query;
+
+                       if (currentControlText.Length > Element.MaxLength)
+                               Control.SetQuery(currentControlText.Substring(0, Element.MaxLength), false);
+               }
+
+               void UpdateInputType()
+               {
+                       SearchBar model = Element;
+                       var keyboard = model.Keyboard;
+
+                       _inputType = keyboard.ToInputType();
+                       if (!(keyboard is Internals.CustomKeyboard))
+                       {
+                               if (model.IsSet(InputView.IsSpellCheckEnabledProperty))
+                               {
+                                       if ((_inputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)
+                                       {
+                                               if (!model.IsSpellCheckEnabled)
+                                                       _inputType = _inputType | InputTypes.TextFlagNoSuggestions;
+                                       }
+                               }
+                       }
+                       Control.SetInputType(_inputType);
+
+                       if (keyboard == Keyboard.Numeric)
+                       {
+                               _editText = _editText ?? Control.GetChildrenOfType<EditText>().FirstOrDefault();
+                               if(_editText != null)
+                                       _editText.KeyListener = GetDigitsKeyListener(_inputType);
+                       }
+               }
+
+               protected virtual 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);
+               }
        }
 }
\ No newline at end of file
index e04b66d..65f796a 100644 (file)
@@ -2,6 +2,7 @@
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Media;
+using Xamarin.Forms.Internals;
 using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
 using Specifics = Xamarin.Forms.PlatformConfiguration.WindowsSpecific.SearchBar;
 
@@ -73,6 +74,10 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateAlignment();
                        else if (e.PropertyName == Specifics.IsSpellCheckEnabledProperty.PropertyName)
                                UpdateIsSpellCheckEnabled();
+                       else if(e.PropertyName == InputView.MaxLengthProperty.PropertyName)
+                               UpdateMaxLength();
+                       else if(e.PropertyName == InputView.IsSpellCheckEnabledProperty.PropertyName)
+                               UpdateInputScope();
                }
 
                void OnControlLoaded(object sender, RoutedEventArgs routedEventArgs)
@@ -92,6 +97,8 @@ namespace Xamarin.Forms.Platform.UWP
                        UpdatePlaceholderColor();
                        UpdateBackgroundColor();
                        UpdateIsSpellCheckEnabled();
+                       UpdateInputScope();
+                       UpdateMaxLength();
 
                        // If the Forms VisualStateManager is in play or the user wants to disable the Forms legacy
                        // color stuff, then the underlying textbox should just use the Forms VSM states
@@ -226,6 +233,44 @@ namespace Xamarin.Forms.Platform.UWP
                                _queryTextBox.IsSpellCheckEnabled = Element.OnThisPlatform().GetIsSpellCheckEnabled();
                }
 
+               void UpdateMaxLength()
+               {
+                       if (_queryTextBox == null)
+                               return;
+
+                       _queryTextBox.MaxLength = Element.MaxLength;
+
+                       var currentControlText = Control.Text;
+
+                       if (currentControlText.Length > Element.MaxLength)
+                               Control.Text = currentControlText.Substring(0, Element.MaxLength);
+               }
+
+               void UpdateInputScope()
+               {
+                       if(_queryTextBox == null)
+                               return;
+
+                       InputView model = Element;
+
+                       if (model.Keyboard is CustomKeyboard custom)
+                       {
+                               _queryTextBox.IsTextPredictionEnabled = (custom.Flags & KeyboardFlags.Suggestions) != 0;
+                               _queryTextBox.IsSpellCheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) != 0;
+                       }
+                       else
+                       {
+                               _queryTextBox.ClearValue(TextBox.IsTextPredictionEnabledProperty);
+
+                               if (model.IsSet(InputView.IsSpellCheckEnabledProperty))
+                                       _queryTextBox.IsSpellCheckEnabled = model.IsSpellCheckEnabled;
+                               else
+                                       _queryTextBox.ClearValue(TextBox.IsSpellCheckEnabledProperty);
+                       }
+
+                       _queryTextBox.InputScope = model.Keyboard.ToInputScope();
+               }
+
                protected override void UpdateBackgroundColor()
                {
                        if (_queryTextBox == null)
index 88a4558..8c23153 100644 (file)
@@ -5,7 +5,7 @@ namespace Xamarin.Forms.Platform.iOS
 {
        public static class Extensions
        {
-               public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard)
+               public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard keyboard)
                {
                        textInput.AutocapitalizationType = UITextAutocapitalizationType.None;
                        textInput.AutocorrectionType = UITextAutocorrectionType.No;
index 4864c3a..abf431d 100644 (file)
@@ -1,6 +1,8 @@
 using System;
 using System.ComponentModel;
 using System.Drawing;
+using CoreGraphics;
+using Foundation;
 using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
@@ -18,6 +20,8 @@ namespace Xamarin.Forms.Platform.iOS
                string _typedText;
                bool _useLegacyColorManagement;
 
+               UIToolbar _numericAccessoryView;
+
                IElementController ElementController => Element as IElementController;
 
                protected override void Dispose(bool disposing)
@@ -29,6 +33,7 @@ namespace Xamarin.Forms.Platform.iOS
                                        Control.CancelButtonClicked -= OnCancelClicked;
                                        Control.SearchButtonClicked -= OnSearchButtonClicked;
                                        Control.TextChanged -= OnTextChanged;
+                                       Control.ShouldChangeTextInRange -= ShouldChangeText;
 
                                        Control.OnEditingStarted -= OnEditingEnded;
                                        Control.OnEditingStopped -= OnEditingStarted;
@@ -59,6 +64,7 @@ namespace Xamarin.Forms.Platform.iOS
                                        Control.CancelButtonClicked += OnCancelClicked;
                                        Control.SearchButtonClicked += OnSearchButtonClicked;
                                        Control.TextChanged += OnTextChanged;
+                                       Control.ShouldChangeTextInRange += ShouldChangeText;
 
                                        Control.OnEditingStarted += OnEditingStarted;
                                        Control.OnEditingStopped += OnEditingEnded;
@@ -71,6 +77,8 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateCancelButton();
                                UpdateAlignment();
                                UpdateTextColor();
+                               UpdateMaxLength();
+                               UpdateKeyboard();
                        }
 
                        base.OnElementChanged(e);
@@ -104,6 +112,12 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateAlignment();
                        else if (e.PropertyName == VisualElement.FlowDirectionProperty.PropertyName)
                                UpdateAlignment();
+                       else if(e.PropertyName == Xamarin.Forms.InputView.MaxLengthProperty.PropertyName)
+                               UpdateMaxLength();
+                       else if(e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName)
+                               UpdateKeyboard();
+                       else if(e.PropertyName == Xamarin.Forms.InputView.IsSpellCheckEnabledProperty.PropertyName)
+                               UpdateKeyboard();
                }
 
                protected override void SetBackgroundColor(Color color)
@@ -294,5 +308,60 @@ namespace Xamarin.Forms.Platform.iOS
                                _textField.TextColor = targetColor.IsDefault ? _defaultTextColor : targetColor.ToUIColor();
                        }
                }
+
+               void UpdateMaxLength()
+               {
+                       var currentControlText = Control.Text;
+
+                       if (currentControlText.Length > Element.MaxLength)
+                               Control.Text = currentControlText.Substring(0, Element.MaxLength);
+               }
+
+               bool ShouldChangeText(UISearchBar searchBar, NSRange range, string text)
+               {
+                       var newLength = searchBar?.Text?.Length + text.Length - range.Length;
+                       return newLength <= Element?.MaxLength;
+               }
+
+               void UpdateKeyboard()
+               {
+                       var keyboard = Element.Keyboard;
+                       Control.ApplyKeyboard(keyboard);
+                       if (!(keyboard is Internals.CustomKeyboard))
+                       {
+                               if (Element.IsSet(Xamarin.Forms.InputView.IsSpellCheckEnabledProperty))
+                               {
+                                       if (!Element.IsSpellCheckEnabled)
+                                       {
+                                               Control.SpellCheckingType = UITextSpellCheckingType.No;
+                                       }
+                               }
+                       }
+
+                       // iPhone does not have an enter key on numeric keyboards
+                       if (Device.Idiom == TargetIdiom.Phone && (keyboard == Keyboard.Numeric || keyboard == Keyboard.Telephone))
+                       {
+                               _numericAccessoryView = _numericAccessoryView ?? CreateNumericKeyboardAccessoryView();
+                               Control.InputAccessoryView = _numericAccessoryView;
+                       }
+                       else
+                       {
+                               Control.InputAccessoryView = null;
+                       }
+
+                       Control.ReloadInputViews();
+               }
+
+               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;
+               }
        }
 }
\ No newline at end of file