From 87e1c75e897c60e42e91db7af492482ab3d5ffec Mon Sep 17 00:00:00 2001 From: ShaneN Date: Fri, 24 Aug 2018 11:36:53 -0600 Subject: [PATCH] [iOS] fixes #3525 - add epsilon for float compare and make region immutable --- .../Issue3525.cs | 78 +++++++++++++++++++ ...rin.Forms.Controls.Issues.Shared.projitems | 1 + Xamarin.Forms.Core/Region.cs | 26 +++++-- .../Extensions/LabelExtensions.cs | 7 +- 4 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3525.cs diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3525.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3525.cs new file mode 100644 index 00000000000..9f9b7e90176 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3525.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Threading.Tasks; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + + +#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, 3525, "[iOS] Finicky tap gesture recognition on Spans")] +#if UITEST + [NUnit.Framework.Category(UITestCategories.Gestures)] +#endif + public class Issue3525 : TestContentPage + { + const string kClickCount = "Click Count: "; + const string kClickCountAutomationId = "ClickCount"; + const string kLabelTestAutomationId = "SpanningLabel"; + + protected override void Init() + { + var label = new Label() { Text = kClickCount, AutomationId = kClickCountAutomationId }; + Padding = new Thickness(20); + var layout = new StackLayout { Padding = new Thickness(5, 10) }; + + var formattedString = new FormattedString(); + formattedString.Spans.Add(new Span { Text = "Not Clickable, ", ForegroundColor = Color.Red, FontAttributes = FontAttributes.Bold, LineHeight = 1.8 }); + formattedString.Spans.Add(new Span { Text = Environment.NewLine }); + var span = new Span { Text = "Clickable, " }; + int clickCount = 0; + span.GestureRecognizers.Add(new TapGestureRecognizer + { + Command = new Command(() => + { + clickCount++; + label.Text = $"{kClickCount}{clickCount}"; + }) + }); + + formattedString.Spans.Add(span); + formattedString.Spans.Add(new Span { Text = Environment.NewLine }); + + formattedString.Spans.Add(new Span { Text = "You also cannot click on me sorry about that.", FontAttributes = FontAttributes.Italic, FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)) }); + + layout.Children.Add(new Label { AutomationId = kLabelTestAutomationId, FormattedText = formattedString }); + layout.Children.Add(label); + + this.Title = "Label Demo - Code"; + this.Content = layout; + } + +#if UITEST + [Test] + public void SpanRegionClicking() + { + var label = RunningApp.WaitForElement(kLabelTestAutomationId); + var location = label[0].Rect; + + var lineHeight = location.Height / 3; + var y = location.Y; + RunningApp.TapCoordinates(location.X, y + lineHeight / 2); + RunningApp.TapCoordinates(location.X + 5, y + lineHeight + 5); + RunningApp.TapCoordinates(location.X, y + (lineHeight * 2)); + RunningApp.WaitForElement($"{kClickCount}{1}"); + + } +#endif + } +} 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 62c5e723d77..7d213834bf3 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 @@ -769,6 +769,7 @@ + diff --git a/Xamarin.Forms.Core/Region.cs b/Xamarin.Forms.Core/Region.cs index 88aec9941ed..43e6d62fe07 100644 --- a/Xamarin.Forms.Core/Region.cs +++ b/Xamarin.Forms.Core/Region.cs @@ -1,15 +1,26 @@ using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Xamarin.Forms { public struct Region { - // While Regions are currently rectangular, they could in the future be transformed into any shape. // As such the internals of how it keeps shapes is hidden, so that future internal changes can occur to support shapes // such as circles if required, without affecting anything else. - IList Regions { get; set; } + IReadOnlyList Regions { get; } + readonly Thickness _inflation; + + private Region(IList positions) : this() + { + Regions = new ReadOnlyCollection(positions); + } + + private Region(IList positions, Thickness inflation) : this(positions) + { + _inflation = inflation; + } public static Region FromLines(double[] lineHeights, double maxWidth, double startX, double endX, double startY) { @@ -34,7 +45,7 @@ public static Region FromLines(double[] lineHeights, double maxWidth, double sta else // SingleLine positions.Add(new Rectangle(startX, lineHeightTotal, endX - startX, lineHeights[i])); - return new Region() { Regions = positions }; + return new Region(positions); } public bool Contains(Point pt) @@ -54,8 +65,6 @@ public bool Contains(double x, double y) return false; } - Thickness _inflation; - public Region Deflate() { if (_inflation == null) @@ -74,6 +83,7 @@ public Region Inflate(double left, double top, double right, double bottom) if (Regions == null) return this; + Rectangle[] rectangles = new Rectangle[Regions.Count]; for (int i = 0; i < Regions.Count; i++) { var region = Regions[i]; @@ -87,15 +97,15 @@ public Region Inflate(double left, double top, double right, double bottom) if (i == Regions.Count - 1) // This is the last line region.Height += bottom + top; - Regions[i] = region; + rectangles[i] = region; } - _inflation = new Thickness(_inflation == null ? left : left + _inflation.Left, + var inflation = new Thickness(_inflation == null ? left : left + _inflation.Left, _inflation == null ? top : top + _inflation.Top, _inflation == null ? right : right + _inflation.Right, _inflation == null ? bottom : bottom + _inflation.Bottom); - return this; + return new Region(rectangles, inflation); } } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Extensions/LabelExtensions.cs b/Xamarin.Forms.Platform.iOS/Extensions/LabelExtensions.cs index 6095a749437..e8c2100559a 100644 --- a/Xamarin.Forms.Platform.iOS/Extensions/LabelExtensions.cs +++ b/Xamarin.Forms.Platform.iOS/Extensions/LabelExtensions.cs @@ -74,7 +74,9 @@ public static void RecalculateSpanPositions(this NativeLabel control, Label elem var yaxis = startRect.Top; var lineHeights = new List(); - while (yaxis < endRect.Bottom) + + while( (endRect.Bottom - yaxis) > 0.001 ) + //while (yaxis < endRect.Bottom) { double lineHeight; if (yaxis == startRect.Top) // First Line @@ -93,7 +95,8 @@ public static void RecalculateSpanPositions(this NativeLabel control, Label elem yaxis += (float)lineHeight; } - ((ISpatialElement)span).Region = Region.FromLines(lineHeights.ToArray(), finalSize.Width, startRect.X, endRect.X, startRect.Top).Inflate(10); + ((ISpatialElement)span).Region = Region.FromLines(lineHeights.ToArray(), finalSize.Width, startRect.X, endRect.X, startRect.Top) + .Inflate(10); // update current location currentLocation += length;