Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ static void ProcessRecognizerHandlerTap(
if (platformRecognizer == null)
{
if (virtualView == element)
return new Point((int)originPoint.X, (int)originPoint.Y);
return new Point(originPoint.X, originPoint.Y);

var targetViewScreenLocation = virtualView.GetLocationOnScreen();

Expand Down Expand Up @@ -229,7 +229,7 @@ static void ProcessRecognizerHandlerTap(
if (result == null)
return null;

return new Point((int)result.Value.X, (int)result.Value.Y);
return new Point(result.Value.X, result.Value.Y);
}

protected virtual List<UIGestureRecognizer?>? GetPlatformRecognizer(IGestureRecognizer recognizer)
Expand Down
98 changes: 98 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue35943.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 35943, "[iOS, MacCatalyst] GetPosition Truncates Fractional Coordinates to Integers on TappedEvent", PlatformAffected.iOS)]
public class Issue35943 : ContentPage
{
public Issue35943()
{
// A half-point left/top margin places this view at a fractional UIKit position (X=0.5, Y=0.5
// relative to the container). Any tap at an integer screen coordinate will therefore produce
// fractional coordinates when expressed in this view's local coordinate system.
var referenceBox = new BoxView
{
Color = Colors.CornflowerBlue,
WidthRequest = 10,
HeightRequest = 10,
HorizontalOptions = LayoutOptions.Start,
Margin = new Thickness(0.5, 0.5, 0, 0),
AutomationId = "ReferenceBox"
};

var instructionLabel = new Label
{
Text = "Tap the red box. Coordinates relative to the blue box should be fractional.",
AutomationId = "InstructionLabel"
};

// resultLabel shows human-readable output; statusLabel holds the AutomationId the test waits for.
// They are separate because AutomationId may only be set once on iOS/MacCatalyst.
var resultLabel = new Label
{
Text = "Tap the red box",
AutomationId = "ResultLabel"
};

var statusLabel = new Label { Text = string.Empty };
Comment on lines +27 to +35

var tapTarget = new BoxView
{
Color = Colors.Tomato,
WidthRequest = 200,
HeightRequest = 200,
HorizontalOptions = LayoutOptions.Start,
AutomationId = "TapTarget"
};

var tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += (s, e) =>
{
var position = e.GetPosition(relativeTo: referenceBox);

if (position is null)
{
resultLabel.Text = "Failure: position is null";
if (string.IsNullOrEmpty(statusLabel.AutomationId))
statusLabel.AutomationId = "Failure";
statusLabel.Text = "Failure";
Comment on lines +53 to +56
return;
}

// Because referenceBox is at a fractional UIKit position (Margin = 0.5),
// GetPosition(relativeTo: referenceBox) should return coordinates with
// a fractional component. Before the fix, the (int) cast in CalculatePosition
// would truncate e.g. 99.5 → 99, losing sub-pixel precision.
double fracX = Math.Abs(position.Value.X - Math.Truncate(position.Value.X));
double fracY = Math.Abs(position.Value.Y - Math.Truncate(position.Value.Y));
bool hasFractionalPrecision = fracX > 0.01 || fracY > 0.01;

if (hasFractionalPrecision)
{
resultLabel.Text = $"Success: X={position.Value.X:F4}, Y={position.Value.Y:F4}";
if (string.IsNullOrEmpty(statusLabel.AutomationId))
statusLabel.AutomationId = "Success";
statusLabel.Text = "Success";
Comment on lines +70 to +73
}
else
{
resultLabel.Text = $"Failure: X={position.Value.X}, Y={position.Value.Y} (expected fractional coordinates)";
if (string.IsNullOrEmpty(statusLabel.AutomationId))
statusLabel.AutomationId = "Failure";
statusLabel.Text = "Failure";
Comment on lines +77 to +80
}
};

tapTarget.GestureRecognizers.Add(tapGesture);

Content = new VerticalStackLayout
{
Children =
{
instructionLabel,
referenceBox,
tapTarget,
resultLabel,
statusLabel
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue35943 : _IssuesUITest
{
public Issue35943(TestDevice device) : base(device) { }

public override string Issue => "[iOS, MacCatalyst] GetPosition Truncates Fractional Coordinates to Integers on TappedEvent";

[Test]
[Category(UITestCategories.Gestures)]
public void GetPositionPreservesFractionalCoordinates()
{
// The tap target is a BoxView. The reference box (ReferenceBox) has a 0.5-point
// margin, placing it at a fractional UIKit coordinate. GetPosition(relativeTo: ReferenceBox)
// should therefore return coordinates with a fractional component.
// Before the fix, an explicit (int) cast in CalculatePosition truncated these values.
App.WaitForElement("TapTarget");

App.Tap("TapTarget");

// "Success" appears when the coordinates have a fractional component;
// "Failure" appears when they are truncated to integers.
App.WaitForElement("Success");
}
}
}
Loading