Unverified Commit 601f48cd authored by Callum Moffat's avatar Callum Moffat Committed by GitHub

InteractiveViewer discrete trackpad panning (#112171)

* InteractiveViewer web trackpad panning

* Address feedback
parent 8cfc6061
...@@ -1788,8 +1788,7 @@ class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescriptio ...@@ -1788,8 +1788,7 @@ class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescriptio
assert(kind != null), assert(kind != null),
assert(device != null), assert(device != null),
assert(position != null), assert(position != null),
assert(scrollDelta != null), assert(scrollDelta != null);
assert(!identical(kind, PointerDeviceKind.trackpad));
@override @override
final Offset scrollDelta; final Offset scrollDelta;
......
...@@ -954,11 +954,57 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid ...@@ -954,11 +954,57 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
_controller.forward(); _controller.forward();
} }
// Handle mousewheel scroll events. // Handle mousewheel and web trackpad scroll events.
void _receivedPointerSignal(PointerSignalEvent event) { void _receivedPointerSignal(PointerSignalEvent event) {
final double scaleChange; final double scaleChange;
if (event is PointerScrollEvent) { if (event is PointerScrollEvent) {
// Ignore left and right scroll. if (event.kind == PointerDeviceKind.trackpad) {
// Trackpad scroll, so treat it as a pan.
widget.onInteractionStart?.call(
ScaleStartDetails(
focalPoint: event.position,
localFocalPoint: event.localPosition,
),
);
final Offset localDelta = PointerEvent.transformDeltaViaPositions(
untransformedEndPosition: event.position + event.scrollDelta,
untransformedDelta: event.scrollDelta,
transform: event.transform,
);
if (!_gestureIsSupported(_GestureType.pan)) {
widget.onInteractionUpdate?.call(ScaleUpdateDetails(
focalPoint: event.position - event.scrollDelta,
localFocalPoint: event.localPosition - event.scrollDelta,
focalPointDelta: -localDelta,
));
widget.onInteractionEnd?.call(ScaleEndDetails());
return;
}
final Offset focalPointScene = _transformationController!.toScene(
event.localPosition,
);
final Offset newFocalPointScene = _transformationController!.toScene(
event.localPosition - localDelta,
);
_transformationController!.value = _matrixTranslate(
_transformationController!.value,
newFocalPointScene - focalPointScene
);
widget.onInteractionUpdate?.call(ScaleUpdateDetails(
focalPoint: event.position - event.scrollDelta,
localFocalPoint: event.localPosition - localDelta,
focalPointDelta: -localDelta
));
widget.onInteractionEnd?.call(ScaleEndDetails());
return;
}
// Ignore left and right mouse wheel scroll.
if (event.scrollDelta.dy == 0.0) { if (event.scrollDelta.dy == 0.0) {
return; return;
} }
......
...@@ -850,12 +850,15 @@ void main() { ...@@ -850,12 +850,15 @@ void main() {
expect(const PointerHoverEvent(kind: PointerDeviceKind.trackpad), isNotNull); expect(const PointerHoverEvent(kind: PointerDeviceKind.trackpad), isNotNull);
// Regression test for https://github.com/flutter/flutter/issues/108176 // Regression test for https://github.com/flutter/flutter/issues/108176
expect(const PointerScrollInertiaCancelEvent(kind: PointerDeviceKind.trackpad), isNotNull); expect(const PointerScrollInertiaCancelEvent(kind: PointerDeviceKind.trackpad), isNotNull);
expect(const PointerScrollEvent(kind: PointerDeviceKind.trackpad), isNotNull);
// The test passes if it compiles. // The test passes if it compiles.
}); });
test('Ensure certain event types are not allowed', () { test('Ensure certain event types are not allowed', () {
expect(() => PointerDownEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); expect(() => PointerDownEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
expect(() => PointerScrollEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); expect(() => PointerMoveEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
expect(() => PointerUpEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
}); });
} }
......
...@@ -1722,6 +1722,50 @@ void main() { ...@@ -1722,6 +1722,50 @@ void main() {
expect(translation2.y, lessThan(translation1.y)); expect(translation2.y, lessThan(translation1.y));
}); });
testWidgets('discrete scroll pointer events', (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,
child: const SizedBox(width: 200.0, height: 200.0),
),
),
),
),
);
expect(transformationController.value.getMaxScaleOnAxis(), 1.0);
Vector3 translation = transformationController.value.getTranslation();
expect(translation.x, 0);
expect(translation.y, 0);
// Send a mouse scroll event, it should cause a scale.
final TestPointer mouse = TestPointer(1, PointerDeviceKind.mouse);
await tester.sendEventToBinding(mouse.hover(tester.getCenter(find.byType(SizedBox))));
await tester.sendEventToBinding(mouse.scroll(const Offset(300, -200)));
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), 2.5);
translation = transformationController.value.getTranslation();
// Will be translated to maintain centering.
expect(translation.x, -150);
expect(translation.y, -150);
// Send a trackpad scroll event, it should cause a pan and no scale.
final TestPointer trackpad = TestPointer(1, PointerDeviceKind.trackpad);
await tester.sendEventToBinding(trackpad.hover(tester.getCenter(find.byType(SizedBox))));
await tester.sendEventToBinding(trackpad.scroll(const Offset(100, -25)));
await tester.pump();
expect(transformationController.value.getMaxScaleOnAxis(), 2.5);
translation = transformationController.value.getTranslation();
expect(translation.x, -250);
expect(translation.y, -125);
});
testWidgets('discrete scale pointer event', (WidgetTester tester) async { testWidgets('discrete scale pointer event', (WidgetTester tester) async {
final TransformationController transformationController = TransformationController(); final TransformationController transformationController = TransformationController();
const double boundaryMargin = 50.0; const double boundaryMargin = 50.0;
......
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