Skip to content

Transform PointerEvents to the local coordinate system of the event receiver#31222

Closed
goderbauer wants to merge 21 commits intoflutter:masterfrom
goderbauer:pointertransform
Closed

Transform PointerEvents to the local coordinate system of the event receiver#31222
goderbauer wants to merge 21 commits intoflutter:masterfrom
goderbauer:pointertransform

Conversation

@goderbauer
Copy link
Member

@goderbauer goderbauer commented Apr 17, 2019

This PR has been updated and re-posted as #32192.

Description

Before this change, PointerEvents would always be expressed in the global coordinate system of the screen. The change adds localPosition and localDelta to PointerEvents, which are expressed in the local coordinate system of the event receiver. This means - among other things - that scrolling now sticks to your finger even if the Scrollable has been scaled up or down. Furthermore, a rotated scrollable can now be scrolled by moving the finger in the (rotated) direction of the scrollview.

Furthermore, this change also fixes hit testing for perspective transforms. For example, if a view is rotated along its x axis, buttons now continue to react to touch events:

image

Related Issues

Fixes #29916
Fixes #18408
Fixes #31958

Tests

I added the following tests:

  • wrapping HitTestResult and its subclasses
  • transforming PointerEvents and related helper methods
  • PointerRouter transforms events before delivering them to the route
  • PointerSignalResolver delivers the transformed event
  • Tests for changed GestureRecognizers to ensure they work in transformed subtrees
  • Tests for new MatrixUtil functions
  • Tests for position transforming methods on BoxHitTestResult and SliverHitTestResult
  • Listener receives transformed events for touch/signal/mouse
  • Scrolling now works as expected (sticks to the finger) in transformed subtrees (incl. perspective transform).

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Does your PR require Flutter developers to manually update their apps to accommodate your change?

@goderbauer goderbauer added the framework flutter/packages/flutter repository. See also f: labels. label Apr 17, 2019

import 'events.dart';

typedef HitTest = bool Function(HitTestResult result, Offset position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is only RenderBox's hitTest type... I don't think the gestures library should hard-code this.

}
_pushTransform(transform);
}
final Offset transformedPosition = position == null ? position : PointerEvent.transformPosition(transform, position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. This is why this has Cartesian knowledge...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe all this belongs in BoxHitTestEntry

Copy link
Member Author

@goderbauer goderbauer Apr 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the problem is that this needs to live in the HitTestResult, though, and not in an individual HitTestEntry. (There are no individual HitTestResult classes for Box or Sliver.)

My "excuse" for hard-coding the cartesian knowledge here is that the gesture library already has this cartesian knowledge baked into the events it dispatched: All PointerEvents in the gesture library have a (cartesian) Offset as position:

/// Coordinate of the position of the pointer, in logical pixels in the global
/// coordinate space.
final Offset position;

From that I generalized that the entire hit-testing kinda happens in the cartesian system. There is a small escape hatch for non-cartesian components: If you don't care about the transformed position, a caller can just provide null. This works pretty well in the sliver case, see https://github.com/flutter/flutter/pull/31222/files#diff-4b5ed324ca3ddab0cd1942fc98a42573R1526.

I am not entirely sure how we can move this to BoxHitTesting - we also need to collect the transformation matrix for non-box ancestors (e.g. slivers) to calculate the correct local position for pointer events.

One thing we could do is remove the Offset stuff from the gesture library and have the call-site do the position transforms if they need it. As outlined above, we'd still need to keep the transform matrix, though. I am currently not sure how "cartesian" a transform matrix is.

Let me know if you have a better idea...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on "this needs to live in the HitTestResult"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked offline. We will try to untangle this by implementing separate HitTestResults for Box, Sliver, and other coordinate systems.

@tvolkert
Copy link
Contributor

tvolkert commented May 2, 2019

@goderbauer will this also address #31958?

@tvolkert
Copy link
Contributor

tvolkert commented May 2, 2019

will this also address #31958?

Yep, it does - I patched this in and checked 😄

@goderbauer
Copy link
Member Author

@tvolkert Thanks for checking. I added the issue to the PR description and will add a test for the specific case.

@goderbauer
Copy link
Member Author

I am closing this PR to continue this work in #32192.

@goderbauer goderbauer closed this May 7, 2019
@goderbauer goderbauer deleted the pointertransform branch November 11, 2020 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scaling a widget also scales its drag gestures Scrollables do not obey MediaQuery devicePixelRatio Transforms break UI hit testing

4 participants