From ce40799c9f91d5ce13c8e7e6f5845fb277c625e1 Mon Sep 17 00:00:00 2001 From: Jon Robinson Levy Date: Fri, 14 Feb 2020 19:46:16 +0100 Subject: [PATCH] Fix 8503, 8787 - text in Entry not immediately visible, or visible after IsVisible set to true (#8536) * Adding a testpage for Issue 8503. * Adding isolated and simplified test to Issue8503, without animations. * Adding fix for Issue 8503, by setting VerticalContentAlign on ContentPresenter wrapped by existing ScrollViewer in FormsTextBoxStyle * Adding Test Page for Issue 8787 * Trimming test page 8787. * Improving title for test page 8787 Co-authored-by: Samantha Houts --- .../Issue8503.cs | 372 ++++++++++++++++++ .../Issue8787.cs | 35 ++ .../Issue8787.xaml | 28 ++ ...rin.Forms.Controls.Issues.Shared.projitems | 3 + .../FormsTextBoxStyle.xaml | 8 +- 5 files changed, 442 insertions(+), 4 deletions(-) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8503.cs create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.cs create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.xaml diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8503.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8503.cs new file mode 100644 index 00000000000..ca21e80b16b --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8503.cs @@ -0,0 +1,372 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; +using System.Linq; +using System.Threading.Tasks; +using System.ComponentModel; + + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 8503, "[Bug] Text is not visible on Entry element after animation finished", + PlatformAffected.UWP)] + public class Issue8503 : TestContentPage + { + Entry isolatedEntry; + + protected override void Init() + { + Title = "Issue 8503 [UWP] - text in Entry not visible until receiving focus"; + ScrollView scrollView = new ScrollView(); + + StackLayout layout = new StackLayout { Margin = new Thickness(20, 0) }; + layout.BindingContext = this; + + Label isolatedHeader = new Label { Text = "Isolated Bug", Margin = new Thickness(0, 20), FontSize = 18 }; + Label isolatedInstructions = new Label + { + Text = "This is the bug isolated. To test:\n" + + "Click on the button below, to show Entry for Isolated Bug.\n" + + "An Entry appears.\n" + + "Check that the entry contains text 'This text should be visible, even before acquiring focus'.\n" + + "If it does, the issue is fixed.\n" + + "If it does not, check if the text appears if the entry receives focus.\n" + + "If it does, the bug is not fixed." + }; + isolatedEntry = new Entry { Text = "This text should be visible, even before acquiring focus", IsVisible = false }; + + Button isolatedShowEntryButton = new Button { Text = "Click to show Entry for Isolated Bug" }; + isolatedShowEntryButton.Clicked += IsolatedShowEntryButton_Clicked; + + layout.Children.Add(isolatedHeader); + layout.Children.Add(isolatedInstructions); + layout.Children.Add(isolatedEntry); + layout.Children.Add(isolatedShowEntryButton); + + Label issueHeader = new Label { Text = "Reported Bug", Margin = new Thickness(0, 20), FontSize = 18 }; + Label issueInstructions = new Label + { + Text = "This is the bug as reported with an example\n" + + "Click on the login-action button.\n" + + "An Entry for a UserName appears with a little translating animation.\n" + + "Check that the entry contains text 'user name goes here'.\n" + + "If it does, the issue is fixed.\n" + + "If it does not, check if the text appears if the entry receives focus.\n" + + "If it does, the bug is there." + }; + + LoginAnimateBehavior bugBehavior = new LoginAnimateBehavior + { + EasingType = EEasingType.SinInOut, + AnimationType = EAnimationType.Translation + }; + + StackLayout issueStackLayout = new StackLayout(); + LoginAnimationStackLayout animatedStackLayout = new LoginAnimationStackLayout + { + IsVisible = false + }; + animatedStackLayout.SetBinding(LoginAnimationStackLayout.VisibleProperty, nameof(IsUserNameEntryVisible)); + animatedStackLayout.Behaviors.Add(bugBehavior); + + Entry loginUserNameEntry = new Entry + { + IsEnabled = true + }; + loginUserNameEntry.SetBinding(Entry.TextProperty, nameof(UserName)); + + Button loginButton = new Button + { + Text = "Login_Action", + WidthRequest = 150 + }; + loginButton.Clicked += LoginButton_Clicked; + + animatedStackLayout.Children.Add(loginUserNameEntry); + + issueStackLayout.Children.Add(animatedStackLayout); + issueStackLayout.Children.Add(loginButton); + + layout.Children.Add(issueHeader); + layout.Children.Add(issueInstructions); + layout.Children.Add(issueStackLayout); + + scrollView.Content = layout; + Content = scrollView; + } + + private void IsolatedShowEntryButton_Clicked(object sender, EventArgs e) + { + isolatedEntry.IsVisible = true; + } + + bool isUserNameEntryVisible; + public bool IsUserNameEntryVisible + { + get + { + return isUserNameEntryVisible; + } + set + { + isUserNameEntryVisible = value; + OnPropertyChanged(nameof(IsUserNameEntryVisible)); + } + } + + string userName; + public string UserName + { + get + { + return userName; + } + set + { + userName = value; + OnPropertyChanged(nameof(UserName)); + } + } + + + private void LoginButton_Clicked(object sender, EventArgs e) + { + UserName = "username goes here"; + IsUserNameEntryVisible = true; + } + + public class LoginAnimationStackLayout : StackLayout + { + public static readonly BindableProperty VisibleProperty = BindableProperty.Create(nameof(Visible), typeof(bool), typeof(LoginAnimationStackLayout), defaultBindingMode: BindingMode.TwoWay, defaultValue: false, propertyChanged: (b, o, n) => ((LoginAnimationStackLayout)b).OnVisibileChanged((bool)o, (bool)n)); + + public bool Visible + { + get + { + var obj = GetValue(VisibleProperty); + if (obj == null) + { + return false; + } + + return (bool)obj; + } + set + { + SetValue(VisibleProperty, value); + } + } + + private void OnVisibileChanged(bool oldvalue, bool newvalue) + { + Visible = newvalue; + } + } + + public class LoginAnimateBehavior : BaseAnimationBehavior + { + private static double? yPosition; + + protected override async void AnimateItem(object sender, EventArgs e) + { + PropertyChangedEventArgs prop = e as PropertyChangedEventArgs; + + if (prop == null || !prop.PropertyName.Equals(nameof(LoginAnimationStackLayout.Visible), StringComparison.OrdinalIgnoreCase)) + { + return; + } + + await DoAnimationByType(AnimationType, associatedObject, ToEasing(EasingType), Scale); + } + + private static async Task DoAnimationByType(EAnimationType animationType, View obj, Easing easing, double scale) + { + LoginAnimationStackLayout stack = obj as LoginAnimationStackLayout; + if (stack == null) + { + return; + } + + switch (animationType) + { + case EAnimationType.Translation: + await DoTranslation(stack, obj, easing); + break; + + case EAnimationType.Scale: + await DoScale(stack, easing, scale); + break; + + default: + stack.IsVisible = stack.Visible; + break; + } + } + + private static async Task DoTranslation(LoginAnimationStackLayout stackLayout, View obj, Easing easing) + { + if (stackLayout.Visible) + { + stackLayout.IsVisible = true; + + if (!yPosition.HasValue) + { + yPosition = obj.TranslationY; + } + + await stackLayout.TranslateTo(obj.TranslationX, obj.TranslationY - 10, 10, easing); + await stackLayout.TranslateTo(obj.TranslationX, obj.TranslationY + 10, 900, easing); + } + else + { + if (!yPosition.HasValue) + { + yPosition = obj.TranslationY; + } + + await stackLayout.TranslateTo(obj.TranslationX, obj.TranslationY, 10, easing); + await stackLayout.TranslateTo(obj.TranslationX, obj.TranslationY - 20, 900, easing); + + obj.TranslationY = yPosition.Value; + + stackLayout.IsVisible = false; + } + } + + private static async Task DoScale(LoginAnimationStackLayout stackLayout, Easing easing, double scale) + { + if (stackLayout.Visible) + { + stackLayout.IsVisible = true; + + await stackLayout.ScaleTo(scale, 250, easing); + await stackLayout.ScaleTo(1.00, 250, easing); + } + else + { + await stackLayout.ScaleTo(1.00, 250, easing); + await stackLayout.ScaleTo(scale, 250, easing); + + stackLayout.IsVisible = false; + } + } + } + + public abstract class BaseAnimationBehavior : Behavior + { + protected View associatedObject; + + public static readonly BindableProperty EasingTypeProperty = BindableProperty.Create(nameof(EasingType), typeof(EEasingType), typeof(BaseAnimationBehavior), defaultValue: EEasingType.Linear, propertyChanged: (b, o, n) => OnEasingFunctionChanged(b, (EEasingType)o, (EEasingType)n)); + public static readonly BindableProperty ScaleProperty = BindableProperty.Create(nameof(Scale), typeof(double), typeof(BaseAnimationBehavior), defaultValue: 1.25); + public static readonly BindableProperty AnimationTypeProperty = BindableProperty.Create(nameof(AnimationType), typeof(EAnimationType), typeof(BaseAnimationBehavior), defaultValue: default(EAnimationType)); + + public EEasingType EasingType + { + get { return (EEasingType)GetValue(EasingTypeProperty); } + set { SetValue(EasingTypeProperty, value); } + } + + public double Scale + { + get { return (double)GetValue(ScaleProperty); } + set { SetValue(ScaleProperty, value); } + } + + public EAnimationType AnimationType + { + get + { + EAnimationType animationType = default(EAnimationType); + string data = GetValue(AnimationTypeProperty)?.ToString(); + + Enum.TryParse(data, out animationType); + + return animationType; + } + set { SetValue(AnimationTypeProperty, value); } + } + + protected override void OnAttachedTo(View bindable) + { + associatedObject = bindable; + associatedObject.PropertyChanged += AnimateItem; + } + + protected override void OnDetachingFrom(View bindable) + { + associatedObject.PropertyChanged -= AnimateItem; + } + + protected static Easing ToEasing(EEasingType easingType) + { + switch (easingType) + { + case EEasingType.BounceIn: + return Easing.BounceIn; + case EEasingType.BounceOut: + return Easing.BounceOut; + case EEasingType.CubicIn: + return Easing.CubicIn; + case EEasingType.CubicInOut: + return Easing.CubicInOut; + case EEasingType.CubicOut: + return Easing.CubicOut; + case EEasingType.Linear: + return Easing.Linear; + case EEasingType.SinIn: + return Easing.SinIn; + case EEasingType.SinInOut: + return Easing.SinInOut; + case EEasingType.SinOut: + return Easing.SinOut; + case EEasingType.SpringIn: + return Easing.SpringIn; + case EEasingType.SpringOut: + return Easing.SpringOut; + default: + return Easing.Linear; + } + } + + private static void OnEasingFunctionChanged(BindableObject bindable, EEasingType oldvalue, EEasingType newvalue) + { + var obj = bindable as BaseAnimationBehavior; + if (obj == null) + { + return; + } + + obj.EasingType = newvalue; + } + + protected abstract void AnimateItem(object sender, EventArgs e); + } + + public enum EAnimationType + { + None, + Translation, + Rotate, + Scale + } + + public enum EEasingType + { + Linear, + SinOut, + SinIn, + SinInOut, + CubicIn, + CubicOut, + CubicInOut, + BounceOut, + BounceIn, + SpringIn, + SpringOut, + } + } +} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.cs new file mode 100644 index 00000000000..9bc34296c22 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; +using System.Linq; +using System.Threading.Tasks; + + +#if UITEST +using Xamarin.UITest; +using NUnit.Framework; +using Xamarin.Forms.Core.UITests; +#endif +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 8787, "[Bug] Entry text initially invisible on UWP", + PlatformAffected.UWP)] + public partial class Issue8787 : TestContentPage + { + public Issue8787() + { +#if APP + InitializeComponent(); +#endif + } + + protected override void Init() + { + } + } +} + diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.xaml b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.xaml new file mode 100644 index 00000000000..43be60131ae --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8787.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index 1005f2b7b2d..e66a06d94e0 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -15,6 +15,7 @@ Code + Issue8902.xaml @@ -156,6 +157,7 @@ + Issue8529_1.xaml @@ -1375,6 +1377,7 @@ MSBuild:UpdateDesignTimeXaml + Designer MSBuild:UpdateDesignTimeXaml diff --git a/Xamarin.Forms.Platform.UAP/FormsTextBoxStyle.xaml b/Xamarin.Forms.Platform.UAP/FormsTextBoxStyle.xaml index fb0809d91b2..8b027a365f1 100644 --- a/Xamarin.Forms.Platform.UAP/FormsTextBoxStyle.xaml +++ b/Xamarin.Forms.Platform.UAP/FormsTextBoxStyle.xaml @@ -211,8 +211,7 @@ ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}" FontWeight="Normal" Margin="0,0,0,8" Grid.Row="0" Visibility="Collapsed" x:DeferLoadStrategy="Lazy" /> - + VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled"> + +