[go: nahoru, domu]

Skip to content

Commit

Permalink
InteractiveViewer discrete trackpad panning (#112171)
Browse files Browse the repository at this point in the history
* InteractiveViewer web trackpad panning

* Address feedback
  • Loading branch information
moffatman committed Dec 12, 2022
1 parent 8cfc606 commit 601f48c
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
3 changes: 1 addition & 2 deletions packages/flutter/lib/src/gestures/events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1788,8 +1788,7 @@ class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescriptio
assert(kind != null),
assert(device != null),
assert(position != null),
assert(scrollDelta != null),
assert(!identical(kind, PointerDeviceKind.trackpad));
assert(scrollDelta != null);

@override
final Offset scrollDelta;
Expand Down
50 changes: 48 additions & 2 deletions packages/flutter/lib/src/widgets/interactive_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -954,11 +954,57 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
_controller.forward();
}

// Handle mousewheel scroll events.
// Handle mousewheel and web trackpad scroll events.
void _receivedPointerSignal(PointerSignalEvent event) {
final double scaleChange;
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) {
return;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/flutter/test/gestures/events_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -850,12 +850,15 @@ void main() {
expect(const PointerHoverEvent(kind: PointerDeviceKind.trackpad), isNotNull);
// Regression test for https://github.com/flutter/flutter/issues/108176
expect(const PointerScrollInertiaCancelEvent(kind: PointerDeviceKind.trackpad), isNotNull);

expect(const PointerScrollEvent(kind: PointerDeviceKind.trackpad), isNotNull);
// The test passes if it compiles.
});

test('Ensure certain event types are not allowed', () {
expect(() => PointerDownEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
expect(() => PointerScrollEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
expect(() => PointerMoveEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
expect(() => PointerUpEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError);
});
}

Expand Down
44 changes: 44 additions & 0 deletions packages/flutter/test/widgets/interactive_viewer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,50 @@ void main() {
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 {
final TransformationController transformationController = TransformationController();
const double boundaryMargin = 50.0;
Expand Down

0 comments on commit 601f48c

Please sign in to comment.