Unverified Commit 9114f445 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Add localFocalPoint to ScaleDetector (#33955)

parent d759197d
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:vector_math/vector_math_64.dart';
import 'arena.dart'; import 'arena.dart';
import 'constants.dart'; import 'constants.dart';
import 'events.dart'; import 'events.dart';
...@@ -34,15 +36,32 @@ class ScaleStartDetails { ...@@ -34,15 +36,32 @@ class ScaleStartDetails {
/// Creates details for [GestureScaleStartCallback]. /// Creates details for [GestureScaleStartCallback].
/// ///
/// The [focalPoint] argument must not be null. /// The [focalPoint] argument must not be null.
ScaleStartDetails({ this.focalPoint = Offset.zero }) ScaleStartDetails({ this.focalPoint = Offset.zero, Offset localFocalPoint, })
: assert(focalPoint != null); : assert(focalPoint != null), localFocalPoint = localFocalPoint ?? focalPoint;
/// The initial focal point of the pointers in contact with the screen. /// The initial focal point of the pointers in contact with the screen.
///
/// Reported in global coordinates. /// Reported in global coordinates.
///
/// See also:
///
/// * [localFocalPoint], which is the same value reported in local
/// coordinates.
final Offset focalPoint; final Offset focalPoint;
/// The initial focal point of the pointers in contact with the screen.
///
/// Reported in local coordinates. Defaults to [focalPoint] if not set in the
/// constructor.
///
/// See also:
///
/// * [focalPoint], which is the same value reported in global
/// coordinates.
final Offset localFocalPoint;
@override @override
String toString() => 'ScaleStartDetails(focalPoint: $focalPoint)'; String toString() => 'ScaleStartDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint)';
} }
/// Details for [GestureScaleUpdateCallback]. /// Details for [GestureScaleUpdateCallback].
...@@ -54,6 +73,7 @@ class ScaleUpdateDetails { ...@@ -54,6 +73,7 @@ class ScaleUpdateDetails {
/// argument must be greater than or equal to zero. /// argument must be greater than or equal to zero.
ScaleUpdateDetails({ ScaleUpdateDetails({
this.focalPoint = Offset.zero, this.focalPoint = Offset.zero,
Offset localFocalPoint,
this.scale = 1.0, this.scale = 1.0,
this.horizontalScale = 1.0, this.horizontalScale = 1.0,
this.verticalScale = 1.0, this.verticalScale = 1.0,
...@@ -62,13 +82,30 @@ class ScaleUpdateDetails { ...@@ -62,13 +82,30 @@ class ScaleUpdateDetails {
assert(scale != null && scale >= 0.0), assert(scale != null && scale >= 0.0),
assert(horizontalScale != null && horizontalScale >= 0.0), assert(horizontalScale != null && horizontalScale >= 0.0),
assert(verticalScale != null && verticalScale >= 0.0), assert(verticalScale != null && verticalScale >= 0.0),
assert(rotation != null); assert(rotation != null),
localFocalPoint = localFocalPoint ?? focalPoint;
/// The focal point of the pointers in contact with the screen. /// The focal point of the pointers in contact with the screen.
/// ///
/// Reported in global coordinates. /// Reported in global coordinates.
///
/// See also:
///
/// * [localFocalPoint], which is the same value reported in local
/// coordinates.
final Offset focalPoint; final Offset focalPoint;
/// The focal point of the pointers in contact with the screen.
///
/// Reported in local coordinates. Defaults to [focalPoint] if not set in the
/// constructor.
///
/// See also:
///
/// * [focalPoint], which is the same value reported in global
/// coordinates.
final Offset localFocalPoint;
/// The scale implied by the average distance between the pointers in contact /// The scale implied by the average distance between the pointers in contact
/// with the screen. /// with the screen.
/// ///
...@@ -109,7 +146,7 @@ class ScaleUpdateDetails { ...@@ -109,7 +146,7 @@ class ScaleUpdateDetails {
final double rotation; final double rotation;
@override @override
String toString() => 'ScaleUpdateDetails(focalPoint: $focalPoint, scale: $scale, horizontalScale: $horizontalScale, verticalScale: $verticalScale, rotation: $rotation)'; String toString() => 'ScaleUpdateDetails(focalPoint: $focalPoint, localFocalPoint: $localFocalPoint, scale: $scale, horizontalScale: $horizontalScale, verticalScale: $verticalScale, rotation: $rotation)';
} }
/// Details for [GestureScaleEndCallback]. /// Details for [GestureScaleEndCallback].
...@@ -203,6 +240,8 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -203,6 +240,8 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
_ScaleState _state = _ScaleState.ready; _ScaleState _state = _ScaleState.ready;
Matrix4 _lastTransform;
Offset _initialFocalPoint; Offset _initialFocalPoint;
Offset _currentFocalPoint; Offset _currentFocalPoint;
double _initialSpan; double _initialSpan;
...@@ -245,7 +284,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -245,7 +284,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
@override @override
void addAllowedPointer(PointerEvent event) { void addAllowedPointer(PointerEvent event) {
startTrackingPointer(event.pointer); startTrackingPointer(event.pointer, event.transform);
_velocityTrackers[event.pointer] = VelocityTracker(); _velocityTrackers[event.pointer] = VelocityTracker();
if (_state == _ScaleState.ready) { if (_state == _ScaleState.ready) {
_state = _ScaleState.possible; _state = _ScaleState.possible;
...@@ -272,15 +311,18 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -272,15 +311,18 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
tracker.addPosition(event.timeStamp, event.position); tracker.addPosition(event.timeStamp, event.position);
_pointerLocations[event.pointer] = event.position; _pointerLocations[event.pointer] = event.position;
shouldStartIfAccepted = true; shouldStartIfAccepted = true;
_lastTransform = event.transform;
} else if (event is PointerDownEvent) { } else if (event is PointerDownEvent) {
_pointerLocations[event.pointer] = event.position; _pointerLocations[event.pointer] = event.position;
_pointerQueue.add(event.pointer); _pointerQueue.add(event.pointer);
didChangeConfiguration = true; didChangeConfiguration = true;
shouldStartIfAccepted = true; shouldStartIfAccepted = true;
_lastTransform = event.transform;
} else if (event is PointerUpEvent || event is PointerCancelEvent) { } else if (event is PointerUpEvent || event is PointerCancelEvent) {
_pointerLocations.remove(event.pointer); _pointerLocations.remove(event.pointer);
_pointerQueue.remove(event.pointer); _pointerQueue.remove(event.pointer);
didChangeConfiguration = true; didChangeConfiguration = true;
_lastTransform = event.transform;
} }
_updateLines(); _updateLines();
...@@ -398,6 +440,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -398,6 +440,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
horizontalScale: _horizontalScaleFactor, horizontalScale: _horizontalScaleFactor,
verticalScale: _verticalScaleFactor, verticalScale: _verticalScaleFactor,
focalPoint: _currentFocalPoint, focalPoint: _currentFocalPoint,
localFocalPoint: PointerEvent.transformPosition(_lastTransform, _currentFocalPoint),
rotation: _computeRotationFactor(), rotation: _computeRotationFactor(),
)); ));
}); });
...@@ -406,7 +449,12 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -406,7 +449,12 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
void _dispatchOnStartCallbackIfNeeded() { void _dispatchOnStartCallbackIfNeeded() {
assert(_state == _ScaleState.started); assert(_state == _ScaleState.started);
if (onStart != null) if (onStart != null)
invokeCallback<void>('onStart', () => onStart(ScaleStartDetails(focalPoint: _currentFocalPoint))); invokeCallback<void>('onStart', () {
onStart(ScaleStartDetails(
focalPoint: _currentFocalPoint,
localFocalPoint: PointerEvent.transformPosition(_lastTransform, _currentFocalPoint),
));
});
} }
@override @override
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
void main() {
testWidgets('gets local corrdinates', (WidgetTester tester) async {
final List<ScaleStartDetails> startDetails = <ScaleStartDetails>[];
final List<ScaleUpdateDetails> updateDetails = <ScaleUpdateDetails>[];
final Key redContainer = UniqueKey();
await tester.pumpWidget(
Center(
child: GestureDetector(
onScaleStart: (ScaleStartDetails details) {
print(details);
startDetails.add(details);
},
onScaleUpdate: (ScaleUpdateDetails details) {
print(details);
updateDetails.add(details);
},
child: Container(
key: redContainer,
width: 100,
height: 100,
color: Colors.red,
),
),
),
);
await tester.startGesture(tester.getCenter(find.byKey(redContainer)) - const Offset(20, 20));
final TestGesture pointer2 = await tester.startGesture(tester.getCenter(find.byKey(redContainer)) + const Offset(30, 30));
await pointer2.moveTo(tester.getCenter(find.byKey(redContainer)) + const Offset(20, 20));
expect(updateDetails.single.localFocalPoint, const Offset(50, 50));
expect(updateDetails.single.focalPoint, const Offset(400, 300));
expect(startDetails, hasLength(2));
expect(startDetails.first.localFocalPoint, const Offset(30, 30));
expect(startDetails.first.focalPoint, const Offset(380, 280));
expect(startDetails.last.localFocalPoint, const Offset(50, 50));
expect(startDetails.last.focalPoint, const Offset(400, 300));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment