🚀 Implements globalPosition and localPosition for TapDragEndDetails (#159962)

Resolves #159961

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Alex Li 2024-12-20 17:13:10 +08:00 committed by GitHub
parent a513498487
commit 188d1e1999
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 19 deletions

View File

@ -373,11 +373,14 @@ class TapDragEndDetails with Diagnosticable {
this.velocity = Velocity.zero,
this.primaryVelocity,
required this.consecutiveTapCount,
this.globalPosition = Offset.zero,
Offset? localPosition,
}) : assert(
primaryVelocity == null ||
primaryVelocity == velocity.pixelsPerSecond.dx ||
primaryVelocity == velocity.pixelsPerSecond.dy,
);
),
localPosition = localPosition ?? globalPosition;
/// The velocity the pointer was moving when it stopped contacting the screen.
///
@ -400,12 +403,28 @@ class TapDragEndDetails with Diagnosticable {
/// the number in the series this tap is.
final int consecutiveTapCount;
/// The global position at which the pointer lifted from the screen.
///
/// See also:
///
/// * [localPosition], which is the [globalPosition] transformed to the
/// coordinate space of the event receiver.
final Offset globalPosition;
/// The local position in the coordinate system of the event receiver at which
/// the pointer lifted from the screen.
///
/// Defaults to [globalPosition] if not specified in the constructor.
final Offset localPosition;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Velocity>('velocity', velocity));
properties.add(DiagnosticsProperty<double?>('primaryVelocity', primaryVelocity));
properties.add(DiagnosticsProperty<int>('consecutiveTapCount', consecutiveTapCount));
properties.add(DiagnosticsProperty<Offset>('globalPosition', globalPosition));
properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition));
}
}
@ -900,9 +919,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
_DragState _dragState = _DragState.ready;
PointerEvent? _start;
late OffsetPair _initialPosition;
late OffsetPair _currentPosition;
late double _globalDistanceMoved;
late double _globalDistanceMovedAllAxes;
OffsetPair? _correctedPosition;
// For drag update throttle.
TapDragUpdateDetails? _lastDragUpdateDetails;
@ -962,6 +981,7 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
_globalDistanceMovedAllAxes = 0.0;
_dragState = _DragState.possible;
_initialPosition = OffsetPair(global: event.position, local: event.localPosition);
_currentPosition = _initialPosition;
_deadlineTimer = Timer(_deadline, () => _didExceedDeadlineWithEvent(event));
}
}
@ -1089,6 +1109,7 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
_pastSlopTolerance || _getGlobalDistance(event, _initialPosition) > computedSlop;
if (_dragState == _DragState.accepted) {
_currentPosition = OffsetPair.fromEventPosition(event);
_checkDragUpdate(event);
} else if (_dragState == _DragState.possible) {
if (_start == null) {
@ -1141,27 +1162,31 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
String get debugDescription => 'tap_and_drag';
void _acceptDrag(PointerEvent event) {
assert(_dragState == _DragState.accepted);
if (!_wonArenaForPrimaryPointer) {
return;
}
if (dragStartBehavior == DragStartBehavior.start) {
_initialPosition =
_initialPosition + OffsetPair(global: event.delta, local: event.localDelta);
_initialPosition += OffsetPair(global: event.delta, local: event.localDelta);
_currentPosition = _initialPosition;
}
_checkDragStart(event);
if (event.localDelta != Offset.zero) {
final Matrix4? localToGlobal =
event.transform != null ? Matrix4.tryInvert(event.transform!) : null;
final Offset correctedLocalPosition = _initialPosition.local + event.localDelta;
final Offset localDelta = event.localDelta;
if (localDelta != Offset.zero) {
_currentPosition = OffsetPair.fromEventPosition(event);
final Offset correctedLocalPosition = _initialPosition.local + localDelta;
final Matrix4? localToGlobalTransform =
event.transform == null ? null : Matrix4.tryInvert(event.transform!);
final Offset globalUpdateDelta = PointerEvent.transformDeltaViaPositions(
transform: localToGlobalTransform,
untransformedDelta: localDelta,
untransformedEndPosition: correctedLocalPosition,
untransformedDelta: event.localDelta,
transform: localToGlobal,
);
final OffsetPair updateDelta = OffsetPair(local: event.localDelta, global: globalUpdateDelta);
_correctedPosition = _initialPosition + updateDelta; // Only adds delta for down behaviour
_checkDragUpdate(event);
_correctedPosition = null;
final OffsetPair updateDelta = OffsetPair(local: localDelta, global: globalUpdateDelta);
// Only adds delta for down behaviour
_checkDragUpdate(event, corrected: _initialPosition + updateDelta);
}
}
@ -1253,11 +1278,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
_start = null;
}
void _checkDragUpdate(PointerEvent event) {
final Offset globalPosition =
_correctedPosition != null ? _correctedPosition!.global : event.position;
final Offset localPosition =
_correctedPosition != null ? _correctedPosition!.local : event.localPosition;
void _checkDragUpdate(PointerEvent event, {OffsetPair? corrected}) {
final Offset globalPosition = corrected?.global ?? event.position;
final Offset localPosition = corrected?.local ?? event.localPosition;
final TapDragUpdateDetails details = TapDragUpdateDetails(
sourceTimeStamp: event.timeStamp,
@ -1282,6 +1305,9 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
}
void _checkDragEnd() {
final Offset globalPosition = _currentPosition.global;
final Offset localPosition = _currentPosition.local;
if (_dragUpdateThrottleTimer != null) {
// If there's already an update scheduled, trigger it immediately and
// cancel the timer.
@ -1290,6 +1316,8 @@ sealed class BaseTapAndDragGestureRecognizer extends OneSequenceGestureRecognize
}
final TapDragEndDetails endDetails = TapDragEndDetails(
globalPosition: globalPosition,
localPosition: localPosition,
primaryVelocity: 0.0,
consecutiveTapCount: consecutiveTapCount,
);

View File

@ -1027,4 +1027,30 @@ void main() {
GestureBinding.instance.gestureArena.sweep(1);
expect(events, <String>['down#1']);
});
testGesture('Contains correct positions in the drag end details', (GestureTester tester) {
late TapDragEndDetails tapDragEndDetails;
tapAndDrag =
TapAndHorizontalDragGestureRecognizer()
..dragStartBehavior = DragStartBehavior.down
..eagerVictoryOnDrag = true
..maxConsecutiveTap = 3
..onDragEnd = (TapDragEndDetails details) {
tapDragEndDetails = details;
};
addTearDown(tapAndDrag.dispose);
final TestPointer pointer = TestPointer(5);
final PointerDownEvent pointerDown = pointer.down(const Offset(10.0, 10.0));
tapAndDrag.addPointer(pointerDown);
tester.closeArena(pointer.pointer);
tester.route(pointerDown);
tester.route(pointer.move(const Offset(50.0, 20.0)));
tester.route(pointer.move(const Offset(90.0, 30.0)));
tester.route(pointer.move(const Offset(120.0, 45.0)));
tester.route(pointer.up());
expect(tapDragEndDetails.globalPosition, const Offset(120.0, 45.0));
});
}