From 6909c544d5ef4efefcca26d64c146532053168c5 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Mon, 19 Jul 2021 15:48:17 -0400 Subject: [PATCH] Don't synthesize primary pointer button unless buttons = 0 (#85808) Some embeddings might send pointer events with buttons = 0x2 (right-click) for touch or stylus device kinds. If the primary button is synthesized for those events, they won't behave properly. Without this change it's not possible to trigger "secondary tap" events on a TapGestureRecognizer while using a stylus or on an iPad trackpad, since that recognizer will receive buttons = 0x3. --- AUTHORS | 1 + .../flutter/lib/src/gestures/converter.dart | 2 +- .../test/gestures/gesture_binding_test.dart | 40 ++++++++++++++++--- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index f7a1150b0f..591e37043d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -81,3 +81,4 @@ Marian Triebe Alexis Rouillard Mirko Mucaria Karol Czeryna +Callum Moffat diff --git a/packages/flutter/lib/src/gestures/converter.dart b/packages/flutter/lib/src/gestures/converter.dart index 504dae2f84..5eb673d714 100644 --- a/packages/flutter/lib/src/gestures/converter.dart +++ b/packages/flutter/lib/src/gestures/converter.dart @@ -19,7 +19,7 @@ int _synthesiseDownButtons(int buttons, PointerDeviceKind kind) { case PointerDeviceKind.touch: case PointerDeviceKind.stylus: case PointerDeviceKind.invertedStylus: - return buttons | kPrimaryButton; + return buttons == 0 ? kPrimaryButton : buttons; case PointerDeviceKind.unknown: // We have no information about the device but we know we never want // buttons to be 0 when the pointer is down. diff --git a/packages/flutter/test/gestures/gesture_binding_test.dart b/packages/flutter/test/gestures/gesture_binding_test.dart index c8143834a1..fb2ea9a3de 100644 --- a/packages/flutter/test/gestures/gesture_binding_test.dart +++ b/packages/flutter/test/gestures/gesture_binding_test.dart @@ -175,7 +175,7 @@ void main() { expect(events[1], isA()); }); - test('Should synthesize kPrimaryButton for touch', () { + test('Should synthesize kPrimaryButton for touch when no button is set', () { final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; const PointerDeviceKind kind = PointerDeviceKind.touch; final ui.PointerDataPacket packet = ui.PointerDataPacket( @@ -203,7 +203,35 @@ void main() { expect(events[4].buttons, equals(0)); }); - test('Should synthesize kPrimaryButton for stylus', () { + test('Should not synthesize kPrimaryButton for touch when a button is set', () { + final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; + const PointerDeviceKind kind = PointerDeviceKind.touch; + final ui.PointerDataPacket packet = ui.PointerDataPacket( + data: [ + ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.down, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy), + ], + ); + + final List events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList(); + + expect(events.length, 5); + expect(events[0], isA()); + expect(events[0].buttons, equals(0)); + expect(events[1], isA()); + expect(events[1].buttons, equals(0)); + expect(events[2], isA()); + expect(events[2].buttons, equals(kSecondaryButton)); + expect(events[3], isA()); + expect(events[3].buttons, equals(kSecondaryButton)); + expect(events[4], isA()); + expect(events[4].buttons, equals(0)); + }); + + test('Should synthesize kPrimaryButton for stylus when no button is set', () { final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; for (final PointerDeviceKind kind in [ PointerDeviceKind.stylus, @@ -230,13 +258,13 @@ void main() { expect(events[2], isA()); expect(events[2].buttons, equals(kPrimaryButton)); expect(events[3], isA()); - expect(events[3].buttons, equals(kPrimaryButton | kSecondaryStylusButton)); + expect(events[3].buttons, equals(kSecondaryStylusButton)); expect(events[4], isA()); expect(events[4].buttons, equals(0)); } }); - test('Should synthesize kPrimaryButton for unknown devices', () { + test('Should synthesize kPrimaryButton for unknown devices when no button is set', () { final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; const PointerDeviceKind kind = PointerDeviceKind.unknown; final ui.PointerDataPacket packet = ui.PointerDataPacket( @@ -244,7 +272,7 @@ void main() { ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy), ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy), ui.PointerData(change: ui.PointerChange.down, kind: kind, physicalX: location.dx, physicalY: location.dy), - ui.PointerData(change: ui.PointerChange.move, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy), ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy), ], ); @@ -259,7 +287,7 @@ void main() { expect(events[2], isA()); expect(events[2].buttons, equals(kPrimaryButton)); expect(events[3], isA()); - expect(events[3].buttons, equals(kPrimaryButton)); + expect(events[3].buttons, equals(kSecondaryButton)); expect(events[4], isA()); expect(events[4].buttons, equals(0)); });