diff --git a/packages/flutter/lib/src/gestures/converter.dart b/packages/flutter/lib/src/gestures/converter.dart index 5bb120e051250..fc71a6816c3e5 100644 --- a/packages/flutter/lib/src/gestures/converter.dart +++ b/packages/flutter/lib/src/gestures/converter.dart @@ -40,6 +40,22 @@ class _PointerState { } } +// Add `kPrimaryButton` to [buttons] when a pointer of certain devices is down. +// +// TODO(tongmu): This patch is supposed to be done by embedders. Patching it +// in framework is a workaround before [PointerEventConverter] is moved to embedders. +// https://github.com/flutter/flutter/issues/30454 +int _synthesiseDownButtons(int buttons, PointerDeviceKind kind) { + switch (kind) { + case PointerDeviceKind.touch: + case PointerDeviceKind.stylus: + case PointerDeviceKind.invertedStylus: + return buttons | kPrimaryButton; + default: + return buttons; + } +} + /// Converts from engine pointer data to framework pointer events. /// /// This takes [PointerDataPacket] objects, as received from the engine via @@ -207,8 +223,7 @@ class PointerEventConverter { kind: kind, device: datum.device, position: position, - // TODO(tongmu): Move button patching to embedder, https://github.com/flutter/flutter/issues/30454 - buttons: datum.buttons | kPrimaryButton, + buttons: _synthesiseDownButtons(datum.buttons, kind), obscured: datum.obscured, pressure: datum.pressure, pressureMin: datum.pressureMin, @@ -237,8 +252,7 @@ class PointerEventConverter { device: datum.device, position: position, delta: state.deltaTo(position), - // TODO(tongmu): Move button patching to embedder, https://github.com/flutter/flutter/issues/30454 - buttons: datum.buttons | kPrimaryButton, + buttons: _synthesiseDownButtons(datum.buttons, kind), obscured: datum.obscured, pressure: datum.pressure, pressureMin: datum.pressureMin, @@ -273,8 +287,7 @@ class PointerEventConverter { device: datum.device, position: position, delta: state.deltaTo(position), - // TODO(tongmu): Move button patching to embedder, https://github.com/flutter/flutter/issues/30454 - buttons: datum.buttons | kPrimaryButton, + buttons: _synthesiseDownButtons(datum.buttons, kind), obscured: datum.obscured, pressure: datum.pressure, pressureMin: datum.pressureMin, @@ -421,8 +434,7 @@ class PointerEventConverter { device: datum.device, position: position, delta: state.deltaTo(position), - // TODO(tongmu): Move button patching to embedder, https://github.com/flutter/flutter/issues/30454 - buttons: datum.buttons | kPrimaryButton, + buttons: _synthesiseDownButtons(datum.buttons, kind), obscured: datum.obscured, pressure: datum.pressure, pressureMin: datum.pressureMin, diff --git a/packages/flutter/test/gestures/gesture_binding_test.dart b/packages/flutter/test/gestures/gesture_binding_test.dart index bd30ac32bfd40..37dfbabf83723 100644 --- a/packages/flutter/test/gestures/gesture_binding_test.dart +++ b/packages/flutter/test/gestures/gesture_binding_test.dart @@ -47,7 +47,6 @@ void main() { ui.window.onPointerDataPacket(packet); expect(events.length, 2); expect(events[0].runtimeType, equals(PointerDownEvent)); - expect(events[0].buttons, equals(1)); expect(events[1].runtimeType, equals(PointerUpEvent)); }); @@ -66,9 +65,7 @@ void main() { ui.window.onPointerDataPacket(packet); expect(events.length, 3); expect(events[0].runtimeType, equals(PointerDownEvent)); - expect(events[0].buttons, equals(1)); expect(events[1].runtimeType, equals(PointerMoveEvent)); - expect(events[1].buttons, equals(1)); expect(events[2].runtimeType, equals(PointerUpEvent)); }); @@ -122,9 +119,7 @@ void main() { ui.window.onPointerDataPacket(packet); expect(events.length, 3); expect(events[0].runtimeType, equals(PointerDownEvent)); - expect(events[0].buttons, equals(1)); expect(events[1].runtimeType, equals(PointerMoveEvent)); - expect(events[1].buttons, equals(1)); expect(events[1].delta, equals(const Offset(9.0, 12.0))); expect(events[2].runtimeType, equals(PointerUpEvent)); }); @@ -143,7 +138,6 @@ void main() { ui.window.onPointerDataPacket(packet); expect(events.length, 2); expect(events[0].runtimeType, equals(PointerDownEvent)); - expect(events[0].buttons, equals(1)); expect(events[1].runtimeType, equals(PointerCancelEvent)); }); @@ -165,7 +159,6 @@ void main() { ui.window.onPointerDataPacket(packet); expect(events.length, 2); expect(events[0].runtimeType, equals(PointerDownEvent)); - expect(events[0].buttons, equals(1)); expect(events[1].runtimeType, equals(PointerCancelEvent)); }); @@ -207,7 +200,6 @@ void main() { expect(events[1].runtimeType, equals(PointerHoverEvent)); expect(events[1].delta, equals(const Offset(5.0, 7.0))); expect(events[2].runtimeType, equals(PointerDownEvent)); - expect(events[2].buttons, equals(1)); expect(events[3].runtimeType, equals(PointerCancelEvent)); expect(events[4].runtimeType, equals(PointerHoverEvent)); expect(events[5].runtimeType, equals(PointerRemovedEvent)); @@ -254,10 +246,109 @@ void main() { expect(events[2].runtimeType, equals(PointerScrollEvent)); expect(events[3].runtimeType, equals(PointerHoverEvent)); expect(events[4].runtimeType, equals(PointerDownEvent)); - expect(events[4].buttons, equals(1)); expect(events[5].runtimeType, equals(PointerMoveEvent)); - expect(events[5].buttons, equals(1)); expect(events[5].delta, equals(unexpectedOffset)); expect(events[6].runtimeType, equals(PointerScrollEvent)); }); + + test('Should synthesise kPrimaryButton for touch', () { + 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, 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.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].runtimeType, equals(PointerAddedEvent)); + expect(events[0].buttons, equals(0)); + expect(events[1].runtimeType, equals(PointerHoverEvent)); + expect(events[1].buttons, equals(0)); + expect(events[2].runtimeType, equals(PointerDownEvent)); + expect(events[2].buttons, equals(kPrimaryButton)); + expect(events[3].runtimeType, equals(PointerMoveEvent)); + expect(events[3].buttons, equals(kPrimaryButton)); + expect(events[4].runtimeType, equals(PointerUpEvent)); + expect(events[4].buttons, equals(0)); + + PointerEventConverter.clearPointers(); + }); + + test('Should synthesise kPrimaryButton for stylus', () { + final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; + for (PointerDeviceKind kind in [ + PointerDeviceKind.stylus, + PointerDeviceKind.invertedStylus, + ]) { + + 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, kind: kind, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryStylusButton, 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].runtimeType, equals(PointerAddedEvent)); + expect(events[0].buttons, equals(0)); + expect(events[1].runtimeType, equals(PointerHoverEvent)); + expect(events[1].buttons, equals(0)); + expect(events[2].runtimeType, equals(PointerDownEvent)); + expect(events[2].buttons, equals(kPrimaryButton)); + expect(events[3].runtimeType, equals(PointerMoveEvent)); + expect(events[3].buttons, equals(kPrimaryButton | kSecondaryStylusButton)); + expect(events[4].runtimeType, equals(PointerUpEvent)); + expect(events[4].buttons, equals(0)); + + PointerEventConverter.clearPointers(); + } + }); + + test('Should not synthesise kPrimaryButton for certain devices', () { + final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio; + for (PointerDeviceKind kind in [ + PointerDeviceKind.mouse, + ]) { + 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, kind: kind, buttons: kMiddleMouseButton, physicalX: location.dx, physicalY: location.dy), + ui.PointerData(change: ui.PointerChange.move, kind: kind, buttons: kMiddleMouseButton | kSecondaryMouseButton, 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].runtimeType, equals(PointerAddedEvent)); + expect(events[0].buttons, equals(0)); + expect(events[1].runtimeType, equals(PointerHoverEvent)); + expect(events[1].buttons, equals(0)); + expect(events[2].runtimeType, equals(PointerDownEvent)); + expect(events[2].buttons, equals(kMiddleMouseButton)); + expect(events[3].runtimeType, equals(PointerMoveEvent)); + expect(events[3].buttons, equals(kMiddleMouseButton | kSecondaryMouseButton)); + expect(events[4].runtimeType, equals(PointerUpEvent)); + expect(events[4].buttons, equals(0)); + + PointerEventConverter.clearPointers(); + } + }); }