Entry ClearButtonMode (#5277)
authorAndrei Nitescu <nitescua@yahoo.com>
Thu, 22 Aug 2019 11:54:04 +0000 (14:54 +0300)
committerRui Marinho <me@ruimarinho.net>
Thu, 22 Aug 2019 11:54:04 +0000 (12:54 +0100)
* Entry ClearButtonMode property (#5277)

* Renamed to ClearButtonVisibility

Xamarin.Forms.Controls/CoreGalleryPages/EntryCoreGalleryPage.cs
Xamarin.Forms.Controls/GalleryPages/MaterialEntryGalleryPage.cs
Xamarin.Forms.Core.UITests.Shared/Tests/EntryUITests.cs
Xamarin.Forms.Core/Entry.cs
Xamarin.Forms.Core/InputView.cs
Xamarin.Forms.CustomAttributes/TestAttributes.cs
Xamarin.Forms.Platform.Android/Renderers/EntryRenderer.cs
Xamarin.Forms.Platform.iOS/Renderers/EntryRenderer.cs

index eeb6431..50f3688 100644 (file)
@@ -105,6 +105,13 @@ namespace Xamarin.Forms.Controls
                        isPasswordInputScopeContainer.ContainerLayout.Children.Add(switchPasswordButton);
                        isPasswordInputScopeContainer.ContainerLayout.Children.Add(switchNumericButton);
 
+                       var switchClearBtnVisibilityBtn = new Button { Text = "Toggle ClearButtonVisibility" };
+                       var clearBtnModelContainer = new ViewContainer<Entry>(Test.Entry.ClearButtonVisibility,
+                               new Entry { Text = "I should have clear button visible", ClearButtonVisibility = ClearButtonVisibility.WhileEditing });
+                       switchClearBtnVisibilityBtn.Clicked += (o, a) =>
+                               clearBtnModelContainer.View.ClearButtonVisibility = clearBtnModelContainer.View.ClearButtonVisibility == ClearButtonVisibility.Never ? ClearButtonVisibility.WhileEditing : ClearButtonVisibility.Never;
+                       clearBtnModelContainer.ContainerLayout.Children.Add(switchClearBtnVisibilityBtn);
+
                        Add (isPasswordContainer);
                        Add (completedContainer);
                        Add (placeholderContainer);
@@ -137,6 +144,7 @@ namespace Xamarin.Forms.Controls
                        Add (maxLengthContainer);
                        Add (readOnlyContainer);
                        Add (isPasswordInputScopeContainer);
+                       Add (clearBtnModelContainer);
                }
        }
 }
\ No newline at end of file
index 790d70e..f15620c 100644 (file)
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Linq;
 
 namespace Xamarin.Forms.Controls.GalleryPages
 {
@@ -9,63 +7,75 @@ namespace Xamarin.Forms.Controls.GalleryPages
                public MaterialEntryGalleryPage()
                {
                        Visual = VisualMarker.Material;
-                       Content =
-                               new ScrollView()
-                               {
 
-                                       Content = new StackLayout()
+                       StackLayout layout = null;
+                       layout = new StackLayout()
+                       {
+                               Children =
+                               {
+                                       new Entry()
+                                       {
+                                               Text = "With Text",
+                                               Placeholder = "Placeholder",
+                                       },
+                                       new Entry()
+                                       {
+                                               Placeholder = "Placeholder"
+                                       },
+                                       new Entry()
+                                       {
+                                               Placeholder = "Green Placeholder",
+                                               PlaceholderColor = Color.Green,
+                                       },
+                                       new Entry()
+                                       {
+                                               Placeholder = "Purple Placeholder",
+                                               PlaceholderColor = Color.Purple,
+                                       },
+                                       new Entry()
+                                       {
+                                               Text = "Green TextColor",
+                                               TextColor = Color.Green
+                                       },
+                                       new Entry()
+                                       {
+                                               Text = "Purple TextColor",
+                                               TextColor = Color.Purple
+                                       },
+                                       new Entry()
+                                       {
+                                               Text = "With Text larger font",
+                                               Placeholder = "Placeholder",
+                                               FontSize = 24
+                                       },
+                                       new Entry()
+                                       {
+                                               Text = "Yellow BackgroundColor",
+                                               BackgroundColor = Color.Yellow
+                                       },
+                                       new Entry()
                                        {
-                                               Children =
+                                               Text = "Cyan BackgroundColor",
+                                               BackgroundColor = Color.Cyan
+                                       },
+                                       new Button()
+                                       {
+                                               Text = "Toggle Entry clear button mode",
+                                               Command = new Command(() =>
                                                {
-                                                       new Entry()
-                                                       {
-                                                               Text = "With Text",
-                                                               Placeholder="Placeholder",
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Placeholder = "Placeholder"
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Placeholder = "Green Placeholder",
-                                                               PlaceholderColor = Color.Green,
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Placeholder = "Purple Placeholder",
-                                                               PlaceholderColor = Color.Purple,
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Text = "Green TextColor",
-                                                               TextColor = Color.Green
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Text = "Purple TextColor",
-                                                               TextColor = Color.Purple
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Text = "With Text larger font",
-                                                               Placeholder="Placeholder",
-                                                               FontSize = 24
-                                                       },
-                                                       new Entry()
-                                                       {
-                                                               Text = "Yellow BackgroundColor",
-                                                               BackgroundColor = Color.Yellow
-                                                       },
-                                                       new Entry()
+                                                       foreach(Entry e in layout.Children.OfType<Entry>())
                                                        {
-                                                               Text = "Cyan BackgroundColor",
-                                                               BackgroundColor = Color.Cyan
-                                                       },
-                                               }
+                                                               e.ClearButtonVisibility = e.ClearButtonVisibility == ClearButtonVisibility.Never ? ClearButtonVisibility.WhileEditing : ClearButtonVisibility.Never;
+                                                       }
+                                               })
                                        }
+                               }
+                       };
 
-                               };
+                       Content = new ScrollView()
+                       {
+                               Content = layout
+                       };
                }
        }
 }
index 98b2820..36438d1 100644 (file)
@@ -74,6 +74,18 @@ namespace Xamarin.Forms.Core.UITests
                        Assert.AreEqual(eventLabelText, "Event: Completed (fired 1)");
                }
 
+               [Test]
+               [UiTest(typeof(Entry), "ClearButtonVisibility")]
+               [Category(UITestCategories.ManualReview)]
+               public void ClearButtonVisibility()
+               {
+                       var remote = new StateViewContainerRemote(App, Test.Entry.ClearButtonVisibility, PlatformViewType);
+                       remote.GoTo();
+
+                       App.WaitForElement(q => q.Marked("Toggle ClearButtonVisibility"));
+                       App.Tap(q => q.Marked("Toggle ClearButtonVisibility"));
+               }
+
                protected override void FixtureTeardown()
                {
                        App.NavigateBack();
index 228ebe5..cb95c80 100644 (file)
@@ -43,6 +43,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty SelectionLengthProperty = BindableProperty.Create(nameof(SelectionLength), typeof(int), typeof(Entry), 0, validateValue: (b, v) => (int)v >= 0);
 
+               public static readonly BindableProperty ClearButtonVisibilityProperty = BindableProperty.Create(nameof(ClearButtonVisibility), typeof(ClearButtonVisibility), typeof(Entry), ClearButtonVisibility.Never);
+
                readonly Lazy<PlatformConfigurationRegistry<Entry>> _platformConfigurationRegistry;
 
                public Entry()
@@ -122,7 +124,7 @@ namespace Xamarin.Forms
                        get { return (bool)GetValue(IsTextPredictionEnabledProperty); }
                        set { SetValue(IsTextPredictionEnabledProperty, value); }
                }
-    
+
                public ReturnType ReturnType
                {
                        get => (ReturnType)GetValue(ReturnTypeProperty);
@@ -153,6 +155,12 @@ namespace Xamarin.Forms
                        set => SetValue(ReturnCommandParameterProperty, value);
                }
 
+               public ClearButtonVisibility ClearButtonVisibility
+               {
+                       get => (ClearButtonVisibility)GetValue(ClearButtonVisibilityProperty);
+                       set => SetValue(ClearButtonVisibilityProperty, value);
+               }
+
                double IFontElement.FontSizeDefaultValueCreator() =>
                        Device.GetNamedSize(NamedSize.Default, (Entry)this);
 
index dc5f094..5eed576 100644 (file)
@@ -38,4 +38,10 @@ namespace Xamarin.Forms
                        set { SetValue(IsReadOnlyProperty, value); }
                }
        }
+
+       public enum ClearButtonVisibility
+       {
+               Never,
+               WhileEditing
+       }
 }
\ No newline at end of file
index d6b51f9..4ebeefa 100644 (file)
@@ -541,7 +541,8 @@ namespace Xamarin.Forms.CustomAttributes
                        PasswordColor,
                        MaxLength,
                        IsReadOnly,
-                       IsPasswordNumeric
+                       IsPasswordNumeric,
+                       ClearButtonVisibility
                }
 
                public enum Frame
index 50e0491..e2a1db5 100644 (file)
@@ -2,6 +2,8 @@ using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using Android.Content;
+using Android.Graphics.Drawables;
+using Android.Support.V4.Content;
 using Android.Text;
 using Android.Text.Method;
 using Android.Util;
@@ -61,7 +63,7 @@ namespace Xamarin.Forms.Platform.Android
                }
        }
 
-       public abstract class EntryRendererBase<TControl> : ViewRenderer<Entry, TControl>, ITextWatcher, TextView.IOnEditorActionListener
+       public abstract partial class EntryRendererBase<TControl> : ViewRenderer<Entry, TControl>, ITextWatcher, TextView.IOnEditorActionListener
                where TControl : global::Android.Views.View
        {
                bool _disposed;
@@ -119,6 +121,7 @@ namespace Xamarin.Forms.Platform.Android
 
                void ITextWatcher.AfterTextChanged(IEditable s)
                {
+                       OnAfterTextChanged(s);
                }
 
                void ITextWatcher.BeforeTextChanged(ICharSequence s, int start, int count, int after)
@@ -190,6 +193,8 @@ namespace Xamarin.Forms.Platform.Android
 
                        if (_cursorPositionChangePending || _selectionLengthChangePending)
                                UpdateCursorSelection();
+
+                       UpdateClearBtnOnElementChanged();
                }
 
                protected override void Dispose(bool disposing)
@@ -207,6 +212,7 @@ namespace Xamarin.Forms.Platform.Android
                                {
                                        formsEditContext.OnKeyboardBackPressed -= OnKeyboardBackPressed;
                                        formsEditContext.SelectionChanged -= SelectionChanged;
+                                       ListenForCloseBtnTouch(false);
                                }
                        }
 
@@ -270,6 +276,8 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateCursorSelection();
                        else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
                                UpdateIsReadOnly();
+                       if (e.PropertyName == Entry.ClearButtonVisibilityProperty.PropertyName)
+                               UpdateClearBtnOnPropertyChanged();
 
                        base.OnElementPropertyChanged(sender, e);
                }
@@ -518,4 +526,82 @@ namespace Xamarin.Forms.Platform.Android
                        EditText.Focusable = isReadOnly;
                }
        }
+
+       // Entry clear button management
+       public abstract partial class EntryRendererBase<TControl>
+       {
+               internal override void OnNativeFocusChanged(bool hasFocus)
+               {
+                       base.OnNativeFocusChanged(hasFocus);
+                       UpdateClearBtnOnFocusChanged(hasFocus);
+               }
+
+               void OnAfterTextChanged(IEditable s)
+               {
+                       if (Control.IsFocused)
+                               UpdateClearBtnOnTyping();
+               }
+
+               void EditTextTouched(object sender, TouchEventArgs e)
+               {
+                       MotionEvent me = e.Event;
+                       if (me.Action == MotionEventActions.Up && me.RawX >= EditText.Right - EditText.CompoundPaddingRight)
+                               EditText.Text = null;
+               }
+
+               void UpdateClearBtnOnElementChanged()
+               {
+                       bool showClearBtn = Element.ClearButtonVisibility == ClearButtonVisibility.WhileEditing;
+                       if (showClearBtn && Element.IsFocused)
+                       {
+                               UpdateClearBtn(true);
+                               ListenForCloseBtnTouch(true);
+                       }
+               }
+
+               void UpdateClearBtnOnPropertyChanged()
+               {
+                       bool isFocused = Element.IsFocused;
+                       if (isFocused)
+                       {
+                               bool showClearBtn = Element.ClearButtonVisibility == ClearButtonVisibility.WhileEditing;
+                               UpdateClearBtn(showClearBtn);
+
+                               if (!showClearBtn && isFocused)
+                                       ListenForCloseBtnTouch(false);
+                       }
+               }
+
+               void UpdateClearBtnOnFocusChanged(bool isFocused)
+               {
+                       if (Element.ClearButtonVisibility == ClearButtonVisibility.WhileEditing)
+                       {
+                               UpdateClearBtn(isFocused);
+                               ListenForCloseBtnTouch(isFocused);
+                       }
+               }
+
+               void UpdateClearBtnOnTyping()
+               {
+                       if (Element.ClearButtonVisibility == ClearButtonVisibility.WhileEditing)
+                               UpdateClearBtn(true);
+               }
+
+               void UpdateClearBtn(bool showClearButton)
+               {
+                       Drawable d = showClearButton && (Element.Text?.Length > 0) ? GetCloseButtonDrawable() : null;
+                       EditText.SetCompoundDrawablesWithIntrinsicBounds(null, null, d, null);
+               }
+
+               protected virtual Drawable GetCloseButtonDrawable()
+                       => ContextCompat.GetDrawable(Context, Resource.Drawable.abc_ic_clear_material);
+
+               void ListenForCloseBtnTouch(bool listen)
+               {
+                       if (listen)
+                               EditText.Touch += EditTextTouched;
+                       else
+                               EditText.Touch -= EditTextTouched;
+               }
+       }
 }
index 1643065..6dbcda9 100644 (file)
@@ -146,6 +146,9 @@ namespace Xamarin.Forms.Platform.iOS
 
                        UpdateCursorColor();
                        UpdateIsReadOnly();
+
+                       if (Element.ClearButtonVisibility != ClearButtonVisibility.Never)
+                               UpdateClearButtonVisibility();
                }
 
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -200,6 +203,8 @@ namespace Xamarin.Forms.Platform.iOS
                                UpdateCursorColor();
                        else if (e.PropertyName == Xamarin.Forms.InputView.IsReadOnlyProperty.PropertyName)
                                UpdateIsReadOnly();
+                       else if (e.PropertyName == Entry.ClearButtonVisibilityProperty.PropertyName)
+                               UpdateClearButtonVisibility();
 
                        base.OnElementPropertyChanged(sender, e);
                }
@@ -534,9 +539,14 @@ namespace Xamarin.Forms.Platform.iOS
                        }
                }
 
-        void UpdateIsReadOnly()
-        {
-            Control.UserInteractionEnabled = !Element.IsReadOnly;
-        }
-    }
+               void UpdateIsReadOnly()
+               {
+                       Control.UserInteractionEnabled = !Element.IsReadOnly;
+               }
+
+               void UpdateClearButtonVisibility()
+               {
+                       Control.ClearButtonMode = Element.ClearButtonVisibility == ClearButtonVisibility.WhileEditing ? UITextFieldViewMode.WhileEditing : UITextFieldViewMode.Never;
+               }
+       }
 }