[Enhancement] Entry: Read-only Entry and Editor (#1972)

- fixes #1678
- fixes #4834

* InputView BindableProperty IsReadOnly

* Android Editor and Entry setup

* MacOS Editor and Entry setup

* Tizen Editor and Entry setup

* UAP Editor and Entry setup

* WPF Editor and Entry setup

* iOS Editor and Entry setup

* Issue1678 - TestContentPage added

* Tests added

* Tizen Editor and Entry fix

* UI Test fix

* Android fix for Entry and Editor.

* Unit Tests fix.

* Android Renderer first try.

* MacOS Entry renderer small fix.

* UpdateEditable fix.

* Android Entry and Editor reduced number of calls to BP.

* fix TestAttributes conflicts

* Remove keyboard

* [macOS] relinquish first responder

* [Android] remove call to UpdateCursorSelection

* [UWP] fix tabs
This commit is contained in:
Almir Vuk 2019-01-10 17:21:29 +01:00 коммит произвёл Shane Neuville
Родитель eab7e08cfd
Коммит 5001460dd2
21 изменённых файлов: 219 добавлений и 7 удалений

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

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 1678, "[Enhancement] Entry: Read-only entry", PlatformAffected.All)]
public class Issue1678
: TestContentPage
{
protected override void Init()
{
var entryText = "Entry Lorem Ipsum";
var editorText = "Editor Lorem Ipsum";
var entryDefaults = new Entry { Text = entryText };
var editorDefaults = new Editor { Text = editorText };
var entryReadOnly = new Entry { Text = entryText, IsReadOnly = true };
var editorReadOnly = new Editor { Text = editorText, IsReadOnly = true };
var entryToggleable = new Entry { Text = entryText };
var editorToggleable = new Editor { Text = editorText };
var toggle = new Switch { IsToggled = false };
var stackLayout = new StackLayout();
stackLayout.Children.Add(new Label { Text = "Defaults" });
stackLayout.Children.Add(entryDefaults);
stackLayout.Children.Add(editorDefaults);
stackLayout.Children.Add(new Label { Text = "Read Only" });
stackLayout.Children.Add(entryReadOnly);
stackLayout.Children.Add(editorReadOnly);
stackLayout.Children.Add(new Label { Text = "Toggleable is read only" });
stackLayout.Children.Add(entryToggleable);
stackLayout.Children.Add(editorToggleable);
stackLayout.Children.Add(toggle);
toggle.Toggled += (_, b) =>
{
entryToggleable.IsReadOnly = b.Value;
editorToggleable.IsReadOnly = b.Value;
};
stackLayout.Padding = new Thickness(0, 20, 0, 0);
Content = stackLayout;
}
}
}

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

@ -35,6 +35,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue3788.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1724.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3524.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1678.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2004.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3333.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2338.cs" />

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

@ -40,6 +40,8 @@ namespace Xamarin.Forms.Controls
var maxLengthContainer = new ViewContainer<Editor>(Test.InputView.MaxLength, new Editor { MaxLength = 3 });
var readOnlyContainer = new ViewContainer<Editor>(Test.Editor.IsReadOnly, new Editor { Text = "This is read-only Editor", IsReadOnly = true });
Add(completedContainer);
Add(textContainer);
Add(textChangedContainer);
@ -54,6 +56,7 @@ namespace Xamarin.Forms.Controls
Add(textColorDisabledContainer);
Add(keyboardContainer);
Add(maxLengthContainer);
Add(readOnlyContainer);
}
}
}

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

@ -66,6 +66,7 @@ namespace Xamarin.Forms.Controls
var maxLengthContainer = new ViewContainer<Entry>(Test.InputView.MaxLength, new Entry { MaxLength = 3 });
var readOnlyContainer = new ViewContainer<Entry>(Test.Entry.IsReadOnly, new Entry { Text = "This is read-only Entry", IsReadOnly = true });
var isPasswordInputScopeContainer = new ViewContainer<Entry>(Test.Entry.IsPasswordNumeric, new Entry { Keyboard = Keyboard.Numeric });
var switchPasswordButton = new Button
{
@ -110,6 +111,7 @@ namespace Xamarin.Forms.Controls
Add (placeholderColorDisabledContainer);
Add (passwordColorContainer);
Add (maxLengthContainer);
Add (readOnlyContainer);
Add (isPasswordInputScopeContainer);
}
}

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

@ -30,5 +30,19 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.AreEqual (initialText, oldText);
Assert.AreEqual (finalText, newText);
}
[TestCase(true)]
public void IsReadOnlyTest(bool isReadOnly)
{
Editor editor = new Editor();
editor.SetValue(InputView.IsReadOnlyProperty, isReadOnly);
Assert.AreEqual(isReadOnly, editor.IsReadOnly);
}
[Test]
public void IsReadOnlyDefaultValueTest()
{
Editor editor = new Editor();
Assert.AreEqual(editor.IsReadOnly, false);
}
}
}

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

@ -154,5 +154,20 @@ namespace Xamarin.Forms.Core.UnitTests
Assert.True(result == isEnabled ? true : false);
}
[TestCase(true)]
public void IsReadOnlyTest(bool isReadOnly)
{
Entry entry = new Entry();
entry.SetValue(InputView.IsReadOnlyProperty, isReadOnly);
Assert.AreEqual(isReadOnly, entry.IsReadOnly);
}
[Test]
public void IsReadOnlyDefaultValueTest()
{
Entry entry = new Entry();
Assert.AreEqual(entry.IsReadOnly, false);
}
}
}

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

@ -151,7 +151,9 @@ namespace Xamarin.Forms.Core.UnitTests
new PropertyTestCase<TapGestureRecognizer, int> ("NumberOfTapsRequired", t => t.NumberOfTapsRequired, (t, o) => t.NumberOfTapsRequired = o, () => 1, 3),
new PropertyTestCase<TapGestureRecognizer, object> ("CommandParameter", t => t.CommandParameter, (t, o) => t.CommandParameter = o, () => null, "Test"),
new PropertyTestCase<TapGestureRecognizer, ICommand> ("Command", t => t.Command, (t, o) => t.Command = o, () => null, new Command(()=>{})),
new PropertyTestCase<MasterDetailPage, bool> ("IsGestureEnabled", md => md.IsGestureEnabled, (md, v) => md.IsGestureEnabled = v, () => true, false)
new PropertyTestCase<MasterDetailPage, bool> ("IsGestureEnabled", md => md.IsGestureEnabled, (md, v) => md.IsGestureEnabled = v, () => true, false),
new PropertyTestCase<Entry, bool> ("IsReadOnly", v => v.IsReadOnly, (v, o) => v.IsReadOnly = o, () => false, true),
new PropertyTestCase<Editor, bool> ("IsReadOnly", v => v.IsReadOnly, (v, o) => v.IsReadOnly = o, () => false, true)
};
#pragma warning restore 0414

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

@ -8,6 +8,8 @@ namespace Xamarin.Forms
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create(nameof(MaxLength), typeof(int), typeof(int), int.MaxValue);
public static readonly BindableProperty IsReadOnlyProperty = BindableProperty.Create(nameof(IsReadOnly), typeof(bool), typeof(InputView), false);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
@ -29,5 +31,11 @@ namespace Xamarin.Forms
get { return (bool)GetValue(IsSpellCheckEnabledProperty); }
set { SetValue(IsSpellCheckEnabledProperty, value); }
}
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
}
}

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

@ -507,6 +507,8 @@ namespace Xamarin.Forms.CustomAttributes
FontAttributes,
FontFamily,
FontSize,
MaxLength,
IsReadOnly
}
public enum Entry
@ -530,6 +532,8 @@ namespace Xamarin.Forms.CustomAttributes
TextDisabledColor,
PlaceholderDisabledColor,
PasswordColor,
MaxLength,
IsReadOnly,
IsPasswordNumeric
}

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

@ -90,6 +90,7 @@ namespace Xamarin.Forms.Platform.Android
UpdateMaxLength();
UpdatePlaceholderColor();
UpdatePlaceholderText();
UpdateIsReadOnly();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -116,6 +117,8 @@ namespace Xamarin.Forms.Platform.Android
UpdatePlaceholderText();
else if (e.PropertyName == Editor.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
base.OnElementPropertyChanged(sender, e);
}
@ -247,5 +250,14 @@ namespace Xamarin.Forms.Platform.Android
if (currentControlText.Length > Element.MaxLength)
Control.Text = currentControlText.Substring(0, Element.MaxLength);
}
void UpdateIsReadOnly()
{
bool isReadOnly = !Element.IsReadOnly;
Control.FocusableInTouchMode = isReadOnly;
Control.Focusable = isReadOnly;
Control.SetCursorVisible(isReadOnly);
}
}
}

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

@ -109,6 +109,7 @@ namespace Xamarin.Forms.Platform.Android
UpdateMaxLength();
UpdateImeOptions();
UpdateReturnType();
UpdateIsReadOnly();
if (_cursorPositionChangePending || _selectionLengthChangePending)
UpdateCursorSelection();
@ -183,6 +184,8 @@ namespace Xamarin.Forms.Platform.Android
UpdateCursorSelection();
else if (e.PropertyName == Entry.CursorPositionProperty.PropertyName)
UpdateCursorSelection();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
base.OnElementPropertyChanged(sender, e);
}
@ -332,7 +335,7 @@ namespace Xamarin.Forms.Platform.Android
if (_nativeSelectionIsUpdating || Control == null || Element == null)
return;
if (Control.RequestFocus())
if (!Element.IsReadOnly && Control.RequestFocus())
{
try
{
@ -414,5 +417,14 @@ namespace Xamarin.Forms.Platform.Android
_nativeSelectionIsUpdating = false;
}
}
void UpdateIsReadOnly()
{
bool isReadOnly = !Element.IsReadOnly;
Control.FocusableInTouchMode = isReadOnly;
Control.Focusable = isReadOnly;
Control.SetCursorVisible(isReadOnly);
}
}
}

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

@ -42,6 +42,7 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateTextColor();
UpdateEditable();
UpdateMaxLength();
UpdateIsReadOnly();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -62,6 +63,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateFont();
else if (e.PropertyName == InputView.MaxLengthProperty.PropertyName)
UpdateMaxLength();
else if (e.PropertyName == Xamarin.Forms.InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
}
protected override void SetBackgroundColor(Color color)
@ -148,5 +151,12 @@ namespace Xamarin.Forms.Platform.MacOS
if (currentControlText.Length > Element?.MaxLength)
Control.StringValue = currentControlText.Substring(0, Element.MaxLength);
}
void UpdateIsReadOnly()
{
Control.Editable = !Element.IsReadOnly;
if (Element.IsReadOnly && Control.Window?.FirstResponder == Control.CurrentEditor)
Control.Window?.MakeFirstResponder(null);
}
}
}

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

@ -132,6 +132,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateAlignment();
else if (e.PropertyName == InputView.MaxLengthProperty.PropertyName)
UpdateMaxLength();
else if (e.PropertyName == Xamarin.Forms.InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
base.OnElementPropertyChanged(sender, e);
}
@ -201,7 +203,8 @@ namespace Xamarin.Forms.Platform.MacOS
UpdateFont();
UpdateAlignment();
UpdateMaxLength();
}
UpdateIsReadOnly();
}
void TextFieldFocusChanged(object sender, BoolEventArgs e)
{
@ -289,5 +292,13 @@ namespace Xamarin.Forms.Platform.MacOS
if (currentControlText.Length > Element?.MaxLength)
Control.StringValue = currentControlText.Substring(0, Element.MaxLength);
}
void UpdateIsReadOnly()
{
Control.Editable = !Element.IsReadOnly;
if (Element.IsReadOnly && Control.Window?.FirstResponder == Control.CurrentEditor)
Control.Window?.MakeFirstResponder(null);
}
}
}

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

@ -16,6 +16,7 @@ namespace Xamarin.Forms.Platform.Tizen
RegisterPropertyHandler(InputView.IsSpellCheckEnabledProperty, UpdateIsSpellCheckEnabled);
RegisterPropertyHandler(Editor.PlaceholderProperty, UpdatePlaceholder);
RegisterPropertyHandler(Editor.PlaceholderColorProperty, UpdatePlaceholderColor);
RegisterPropertyHandler(InputView.IsReadOnlyProperty, UpdateIsReadOnly);
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
@ -153,5 +154,10 @@ namespace Xamarin.Forms.Platform.Tizen
return null;
}
void UpdateIsReadOnly()
{
Control.IsEditable = !Element.IsReadOnly;
}
}
}

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

@ -23,6 +23,7 @@ namespace Xamarin.Forms.Platform.Tizen
RegisterPropertyHandler(Entry.IsTextPredictionEnabledProperty, UpdateIsSpellCheckEnabled);
RegisterPropertyHandler(Specific.FontWeightProperty, UpdateFontWeight);
RegisterPropertyHandler(Entry.SelectionLengthProperty, UpdateSelectionLength);
RegisterPropertyHandler(InputView.IsReadOnlyProperty, UpdateIsReadOnly);
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
@ -198,5 +199,10 @@ namespace Xamarin.Forms.Platform.Tizen
return Element.Text.IndexOf(selection, Math.Max(Control.CursorPosition - selection.Length, 0));
}
void UpdateIsReadOnly()
{
Control.IsEditable = !Element.IsReadOnly;
}
}
}

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

@ -62,6 +62,7 @@ namespace Xamarin.Forms.Platform.UWP
UpdateDetectReadingOrderFromContent();
UpdatePlaceholderText();
UpdatePlaceholderColor();
UpdateIsReadOnly();
}
base.OnElementChanged(e);
@ -127,6 +128,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdatePlaceholderText();
else if (e.PropertyName == Editor.PlaceholderColorProperty.PropertyName)
UpdatePlaceholderColor();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
}
void OnLostFocus(object sender, RoutedEventArgs e)
@ -357,5 +360,10 @@ namespace Xamarin.Forms.Platform.UWP
}
}
}
void UpdateIsReadOnly()
{
Control.IsReadOnly = Element.IsReadOnly;
}
}
}

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

@ -61,6 +61,7 @@ namespace Xamarin.Forms.Platform.UWP
UpdateMaxLength();
UpdateDetectReadingOrderFromContent();
UpdateReturnType();
UpdateIsReadOnly();
UpdateInputScope();
if (_cursorPositionChangePending)
@ -136,6 +137,8 @@ namespace Xamarin.Forms.Platform.UWP
UpdateCursorPosition();
else if (e.PropertyName == Entry.SelectionLengthProperty.PropertyName)
UpdateSelectionLength();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
}
protected override void UpdateBackgroundColor()
@ -424,5 +427,10 @@ namespace Xamarin.Forms.Platform.UWP
_nativeSelectionIsUpdating = false;
}
}
void UpdateIsReadOnly()
{
Control.IsReadOnly = Element.IsReadOnly;
}
}
}

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

@ -27,6 +27,7 @@ namespace Xamarin.Forms.Platform.WPF
UpdateTextColor();
UpdateFont();
UpdateMaxLength();
UpdateIsReadOnly();
}
base.OnElementChanged(e);
@ -51,6 +52,8 @@ namespace Xamarin.Forms.Platform.WPF
UpdateFont();
else if (e.PropertyName == InputView.MaxLengthProperty.PropertyName)
UpdateMaxLength();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
}
void NativeOnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
@ -138,5 +141,10 @@ namespace Xamarin.Forms.Platform.WPF
_isDisposed = true;
base.Dispose(disposing);
}
void UpdateIsReadOnly()
{
Control.IsReadOnly = Element.IsReadOnly;
}
}
}

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

@ -35,6 +35,7 @@ namespace Xamarin.Forms.Platform.WPF
UpdateAlignment();
UpdatePlaceholderColor();
UpdateMaxLength();
UpdateIsReadOnly();
}
base.OnElementChanged(e);
@ -66,6 +67,8 @@ namespace Xamarin.Forms.Platform.WPF
UpdatePlaceholderColor();
else if (e.PropertyName == InputView.MaxLengthProperty.PropertyName)
UpdateMaxLength();
else if (e.PropertyName == InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
}
internal override void OnModelFocusChangeRequested(object sender, VisualElement.FocusRequestArgs args)
@ -259,5 +262,10 @@ namespace Xamarin.Forms.Platform.WPF
_isDisposed = true;
base.Dispose(disposing);
}
void UpdateIsReadOnly()
{
Control.IsReadOnly = Element.IsReadOnly;
}
}
}

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

@ -79,6 +79,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateTextAlignment();
UpdateMaxLength();
UpdateAutoSizeOption();
UpdateReadOnly();
UpdateUserInteraction();
}
private void UpdateAutoSizeOption()
@ -134,8 +136,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateKeyboard();
else if (e.PropertyName == Editor.IsTextPredictionEnabledProperty.PropertyName)
UpdateKeyboard();
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
UpdateEditable();
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName || e.PropertyName == Xamarin.Forms.InputView.IsReadOnlyProperty.PropertyName)
UpdateUserInteraction();
else if (e.PropertyName == Editor.TextColorProperty.PropertyName)
UpdateTextColor();
else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName)
@ -277,6 +279,19 @@ namespace Xamarin.Forms.Platform.iOS
return newLength <= Element.MaxLength;
}
void UpdateReadOnly()
{
Control.UserInteractionEnabled = !Element.IsReadOnly;
}
void UpdateUserInteraction()
{
if (Element.IsEnabled && Element.IsReadOnly)
UpdateReadOnly();
else
UpdateEditable();
}
internal class FormsUITextView : UITextView
{
public event EventHandler ContentSizeChanged;

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

@ -126,6 +126,7 @@ namespace Xamarin.Forms.Platform.iOS
UpdateCursorSelection();
UpdateCursorColor();
UpdateIsReadOnly();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
@ -171,6 +172,8 @@ namespace Xamarin.Forms.Platform.iOS
UpdateCursorSelection();
else if (e.PropertyName == Specifics.CursorColorProperty.PropertyName)
UpdateCursorColor();
else if (e.PropertyName == Xamarin.Forms.InputView.IsReadOnlyProperty.PropertyName)
UpdateIsReadOnly();
base.OnElementPropertyChanged(sender, e);
}
@ -478,5 +481,10 @@ namespace Xamarin.Forms.Platform.iOS
_nativeSelectionIsUpdating = false;
}
}
}
void UpdateIsReadOnly()
{
Control.UserInteractionEnabled = !Element.IsReadOnly;
}
}
}