Unverified Commit 7ddf42ea authored by Callum Moffat's avatar Callum Moffat Committed by GitHub

InteractiveViewer parameter to return to pre-3.3 trackpad/Magic Mouse behaviour (#114280)

* trackpadPanShouldActAsZoom

* Address feedback

* Move constant, add blank lines
parent 71f92073
......@@ -288,6 +288,8 @@ class GestureDetector extends StatelessWidget {
this.behavior,
this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.start,
this.trackpadScrollCausesScale = false,
this.trackpadScrollToScaleFactor = kDefaultTrackpadScrollToScaleFactor,
this.supportedDevices,
}) : assert(excludeFromSemantics != null),
assert(dragStartBehavior != null),
......@@ -1014,6 +1016,12 @@ class GestureDetector extends StatelessWidget {
/// If set to null, events from all device types will be recognized. Defaults to null.
final Set<PointerDeviceKind>? supportedDevices;
/// {@macro flutter.gestures.scale.trackpadScrollCausesScale}
final bool trackpadScrollCausesScale;
/// {@macro flutter.gestures.scale.trackpadScrollToScaleFactor}
final Offset trackpadScrollToScaleFactor;
@override
Widget build(BuildContext context) {
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
......@@ -1186,7 +1194,9 @@ class GestureDetector extends StatelessWidget {
..onUpdate = onScaleUpdate
..onEnd = onScaleEnd
..dragStartBehavior = dragStartBehavior
..gestureSettings = gestureSettings;
..gestureSettings = gestureSettings
..trackpadScrollCausesScale = trackpadScrollCausesScale
..trackpadScrollToScaleFactor = trackpadScrollToScaleFactor;
},
);
}
......
......@@ -1173,4 +1173,196 @@ void main() {
),
);
});
testGesture('scale trackpadScrollCausesScale', (GestureTester tester) {
final ScaleGestureRecognizer scale = ScaleGestureRecognizer(
dragStartBehavior: DragStartBehavior.start,
trackpadScrollCausesScale: true
);
bool didStartScale = false;
Offset? updatedFocalPoint;
scale.onStart = (ScaleStartDetails details) {
didStartScale = true;
updatedFocalPoint = details.focalPoint;
};
double? updatedScale;
Offset? updatedDelta;
scale.onUpdate = (ScaleUpdateDetails details) {
updatedScale = details.scale;
updatedFocalPoint = details.focalPoint;
updatedDelta = details.focalPointDelta;
};
bool didEndScale = false;
scale.onEnd = (ScaleEndDetails details) {
didEndScale = true;
};
final TestPointer pointer1 = TestPointer(2, PointerDeviceKind.trackpad);
final PointerPanZoomStartEvent start = pointer1.panZoomStart(Offset.zero);
scale.addPointerPanZoom(start);
tester.closeArena(2);
expect(didStartScale, isFalse);
expect(updatedScale, isNull);
expect(updatedFocalPoint, isNull);
expect(updatedDelta, isNull);
expect(didEndScale, isFalse);
tester.route(start);
expect(didStartScale, isTrue);
didStartScale = false;
expect(updatedScale, isNull);
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedDelta, isNull);
expect(didEndScale, isFalse);
// Zoom in by scrolling up.
tester.route(pointer1.panZoomUpdate(Offset.zero, pan: const Offset(0, -200)));
expect(didStartScale, isFalse);
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedScale, math.e);
updatedScale = null;
expect(updatedDelta, Offset.zero);
updatedDelta = null;
expect(didEndScale, isFalse);
// A horizontal scroll should do nothing.
tester.route(pointer1.panZoomUpdate(Offset.zero, pan: const Offset(200, -200)));
expect(didStartScale, isFalse);
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedScale, math.e);
updatedScale = null;
expect(updatedDelta, Offset.zero);
updatedDelta = null;
expect(didEndScale, isFalse);
// End.
tester.route(pointer1.panZoomEnd());
expect(didStartScale, isFalse);
expect(updatedFocalPoint, isNull);
expect(updatedScale, isNull);
expect(updatedDelta, isNull);
expect(didEndScale, isTrue);
didEndScale = false;
// Try with a different trackpadScrollToScaleFactor
scale.trackpadScrollToScaleFactor = const Offset(1/125, 0);
final PointerPanZoomStartEvent start2 = pointer1.panZoomStart(Offset.zero);
scale.addPointerPanZoom(start2);
tester.closeArena(2);
expect(didStartScale, isFalse);
expect(updatedScale, isNull);
expect(updatedFocalPoint, isNull);
expect(updatedDelta, isNull);
expect(didEndScale, isFalse);
tester.route(start2);
expect(didStartScale, isTrue);
didStartScale = false;
expect(updatedScale, isNull);
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedDelta, isNull);
expect(didEndScale, isFalse);
// Zoom in by scrolling left.
tester.route(pointer1.panZoomUpdate(Offset.zero, pan: const Offset(125, 0)));
expect(didStartScale, isFalse);
didStartScale = false;
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedScale, math.e);
updatedScale = null;
expect(updatedDelta, Offset.zero);
updatedDelta = null;
expect(didEndScale, isFalse);
// A vertical scroll should do nothing.
tester.route(pointer1.panZoomUpdate(Offset.zero, pan: const Offset(125, 125)));
expect(didStartScale, isFalse);
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(updatedScale, math.e);
updatedScale = null;
expect(updatedDelta, Offset.zero);
updatedDelta = null;
expect(didEndScale, isFalse);
// End.
tester.route(pointer1.panZoomEnd());
expect(didStartScale, isFalse);
expect(updatedFocalPoint, isNull);
expect(updatedScale, isNull);
expect(updatedDelta, isNull);
expect(didEndScale, isTrue);
didEndScale = false;
scale.dispose();
});
testGesture('scale ending velocity', (GestureTester tester) {
final ScaleGestureRecognizer scale = ScaleGestureRecognizer(
dragStartBehavior: DragStartBehavior.start,
trackpadScrollCausesScale: true
);
bool didStartScale = false;
Offset? updatedFocalPoint;
scale.onStart = (ScaleStartDetails details) {
didStartScale = true;
updatedFocalPoint = details.focalPoint;
};
bool didEndScale = false;
double? scaleEndVelocity;
scale.onEnd = (ScaleEndDetails details) {
didEndScale = true;
scaleEndVelocity = details.scaleVelocity;
};
final TestPointer pointer1 = TestPointer(2, PointerDeviceKind.trackpad);
final PointerPanZoomStartEvent start = pointer1.panZoomStart(Offset.zero);
scale.addPointerPanZoom(start);
tester.closeArena(2);
expect(didStartScale, isFalse);
expect(updatedFocalPoint, isNull);
expect(didEndScale, isFalse);
tester.route(start);
expect(didStartScale, isTrue);
didStartScale = false;
expect(updatedFocalPoint, Offset.zero);
updatedFocalPoint = null;
expect(didEndScale, isFalse);
// Zoom in by scrolling up.
for (int i = 0; i < 100; i++) {
tester.route(pointer1.panZoomUpdate(
Offset.zero,
pan: Offset(0, i * -10),
timeStamp: Duration(milliseconds: i * 25)
));
}
// End.
tester.route(pointer1.panZoomEnd(timeStamp: const Duration(milliseconds: 2500)));
expect(didStartScale, isFalse);
expect(updatedFocalPoint, isNull);
expect(didEndScale, isTrue);
didEndScale = false;
expect(scaleEndVelocity, moreOrLessEquals(281.41454098027765));
scale.dispose();
});
}
......@@ -1802,6 +1802,78 @@ void main() {
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), 2.5); // capped at maxScale (2.5)
});
testWidgets('trackpadScrollCausesScale', (WidgetTester tester) async {
final TransformationController transformationController = TransformationController();
const double boundaryMargin = 50.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: InteractiveViewer(
boundaryMargin: const EdgeInsets.all(boundaryMargin),
transformationController: transformationController,
trackpadScrollCausesScale: true,
child: const SizedBox(width: 200.0, height: 200.0),
),
),
),
),
);
expect(transformationController.value.getMaxScaleOnAxis(), 1.0);
// Send a vertical scroll.
final TestPointer pointer = TestPointer(1, PointerDeviceKind.trackpad);
final Offset center = tester.getCenter(find.byType(SizedBox));
await tester.sendEventToBinding(pointer.panZoomStart(center));
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), 1.0);
await tester.sendEventToBinding(pointer.panZoomUpdate(center, pan: const Offset(0, -81)));
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.499302500056767));
// Send a horizontal scroll (should have no effect).
await tester.sendEventToBinding(pointer.panZoomUpdate(center, pan: const Offset(81, -81)));
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.499302500056767));
});
testWidgets('Scaling inertia', (WidgetTester tester) async {
final TransformationController transformationController = TransformationController();
const double boundaryMargin = 50.0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: InteractiveViewer(
boundaryMargin: const EdgeInsets.all(boundaryMargin),
transformationController: transformationController,
trackpadScrollCausesScale: true,
child: const SizedBox(width: 200.0, height: 200.0),
),
),
),
),
);
expect(transformationController.value.getMaxScaleOnAxis(), 1.0);
// Send a vertical scroll fling, which will cause inertia.
await tester.trackpadFling(
find.byType(InteractiveViewer),
const Offset(0, -100),
3000
);
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.6487212707001282));
await tester.pump(const Duration(milliseconds: 80));
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.7966838346780103));
await tester.pumpAndSettle();
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.9984509673751225));
await tester.pump(const Duration(seconds: 10));
expect(transformationController.value.getMaxScaleOnAxis(), moreOrLessEquals(1.9984509673751225));
});
});
group('getNearestPointOnLine', () {
......
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