| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:html' as html; |
| import 'dart:js_util' as js_util; |
| |
| import 'package:test/bootstrap/browser.dart'; |
| import 'package:test/test.dart'; |
| import 'package:ui/src/engine.dart' show domRenderer, window; |
| import 'package:ui/src/engine/browser_detection.dart'; |
| import 'package:ui/src/engine/dom_renderer.dart'; |
| import 'package:ui/src/engine/pointer_binding.dart'; |
| import 'package:ui/ui.dart' as ui; |
| |
| const int _kNoButtonChange = -1; |
| const PointerSupportDetector _defaultSupportDetector = PointerSupportDetector(); |
| |
| List<ui.PointerData> _allPointerData(List<ui.PointerDataPacket> packets) { |
| return packets.expand((ui.PointerDataPacket packet) => packet.data).toList(); |
| } |
| |
| typedef _ContextTestBody<T> = void Function(T); |
| |
| void _testEach<T extends _BasicEventContext>( |
| Iterable<T> contexts, |
| String description, |
| _ContextTestBody<T> body, |
| ) { |
| for (final T context in contexts) { |
| if (context.isSupported) { |
| test('${context.name} $description', () { |
| body(context); |
| }); |
| } |
| } |
| } |
| |
| /// Some methods in this class are skipped for iOS-Safari. |
| // TODO(mdebbar): https://github.com/flutter/flutter/issues/60033 |
| bool get isIosSafari => browserEngine == BrowserEngine.webkit && |
| operatingSystem == OperatingSystem.iOs; |
| |
| void main() { |
| internalBootstrapBrowserTest(() => testMain); |
| } |
| |
| void testMain() { |
| final html.Element glassPane = domRenderer.glassPaneElement!; |
| double dpi = 1.0; |
| |
| setUp(() { |
| ensureDomRendererInitialized(); |
| ui.window.onPointerDataPacket = null; |
| dpi = window.devicePixelRatio; |
| }); |
| |
| test('_PointerEventContext generates expected events', () { |
| if (!_PointerEventContext().isSupported) { |
| return; |
| } |
| |
| html.PointerEvent expectCorrectType(html.Event e) { |
| expect(e.runtimeType, equals(html.PointerEvent)); |
| return e as html.PointerEvent; |
| } |
| |
| List<html.PointerEvent> expectCorrectTypes(List<html.Event> events) { |
| return events.map(expectCorrectType).toList(); |
| } |
| |
| final _PointerEventContext context = _PointerEventContext(); |
| html.PointerEvent event; |
| List<html.PointerEvent> events; |
| |
| event = expectCorrectType(context.primaryDown(clientX: 100, clientY: 101)); |
| expect(event.type, equals('pointerdown')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(100)); |
| expect(event.client.y, equals(101)); |
| |
| event = expectCorrectType( |
| context.mouseDown(clientX: 110, clientY: 111, button: 2, buttons: 2)); |
| expect(event.type, equals('pointerdown')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(2)); |
| expect(event.client.x, equals(110)); |
| expect(event.client.y, equals(111)); |
| |
| events = expectCorrectTypes(context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 100, clientX: 120, clientY: 121), |
| _TouchDetails(pointer: 101, clientX: 122, clientY: 123), |
| ])); |
| expect(events.length, equals(2)); |
| expect(events[0].type, equals('pointerdown')); |
| expect(events[0].pointerId, equals(100)); |
| expect(events[0].button, equals(0)); |
| expect(events[0].buttons, equals(1)); |
| expect(events[0].client.x, equals(120)); |
| expect(events[0].client.y, equals(121)); |
| expect(events[1].type, equals('pointerdown')); |
| expect(events[1].pointerId, equals(101)); |
| expect(events[1].button, equals(0)); |
| expect(events[1].buttons, equals(1)); |
| expect(events[1].client.x, equals(122)); |
| expect(events[1].client.y, equals(123)); |
| |
| event = expectCorrectType(context.primaryMove(clientX: 200, clientY: 201)); |
| expect(event.type, equals('pointermove')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(-1)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(200)); |
| expect(event.client.y, equals(201)); |
| |
| event = expectCorrectType(context.mouseMove( |
| clientX: 210, clientY: 211, button: _kNoButtonChange, buttons: 6)); |
| expect(event.type, equals('pointermove')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(-1)); |
| expect(event.buttons, equals(6)); |
| expect(event.client.x, equals(210)); |
| expect(event.client.y, equals(211)); |
| |
| event = expectCorrectType( |
| context.mouseMove(clientX: 212, clientY: 213, button: 2, buttons: 6)); |
| expect(event.type, equals('pointermove')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(6)); |
| expect(event.client.x, equals(212)); |
| expect(event.client.y, equals(213)); |
| |
| event = expectCorrectType( |
| context.mouseMove(clientX: 214, clientY: 215, button: 2, buttons: 1)); |
| expect(event.type, equals('pointermove')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(214)); |
| expect(event.client.y, equals(215)); |
| |
| events = expectCorrectTypes(context.multiTouchMove(const <_TouchDetails>[ |
| _TouchDetails(pointer: 102, clientX: 220, clientY: 221), |
| _TouchDetails(pointer: 103, clientX: 222, clientY: 223), |
| ])); |
| expect(events.length, equals(2)); |
| expect(events[0].type, equals('pointermove')); |
| expect(events[0].pointerId, equals(102)); |
| expect(events[0].button, equals(-1)); |
| expect(events[0].buttons, equals(1)); |
| expect(events[0].client.x, equals(220)); |
| expect(events[0].client.y, equals(221)); |
| expect(events[1].type, equals('pointermove')); |
| expect(events[1].pointerId, equals(103)); |
| expect(events[1].button, equals(-1)); |
| expect(events[1].buttons, equals(1)); |
| expect(events[1].client.x, equals(222)); |
| expect(events[1].client.y, equals(223)); |
| |
| event = expectCorrectType(context.primaryUp(clientX: 300, clientY: 301)); |
| expect(event.type, equals('pointerup')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(300)); |
| expect(event.client.y, equals(301)); |
| |
| event = expectCorrectType( |
| context.mouseUp(clientX: 310, clientY: 311, button: 2)); |
| expect(event.type, equals('pointerup')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(310)); |
| expect(event.client.y, equals(311)); |
| |
| events = expectCorrectTypes(context.multiTouchUp(const <_TouchDetails>[ |
| _TouchDetails(pointer: 104, clientX: 320, clientY: 321), |
| _TouchDetails(pointer: 105, clientX: 322, clientY: 323), |
| ])); |
| expect(events.length, equals(2)); |
| expect(events[0].type, equals('pointerup')); |
| expect(events[0].pointerId, equals(104)); |
| expect(events[0].button, equals(0)); |
| expect(events[0].buttons, equals(0)); |
| expect(events[0].client.x, equals(320)); |
| expect(events[0].client.y, equals(321)); |
| expect(events[1].type, equals('pointerup')); |
| expect(events[1].pointerId, equals(105)); |
| expect(events[1].button, equals(0)); |
| expect(events[1].buttons, equals(0)); |
| expect(events[1].client.x, equals(322)); |
| expect(events[1].client.y, equals(323)); |
| |
| event = expectCorrectType(context.hover(clientX: 400, clientY: 401)); |
| expect(event.type, equals('pointermove')); |
| expect(event.pointerId, equals(1)); |
| expect(event.button, equals(-1)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(400)); |
| expect(event.client.y, equals(401)); |
| |
| events = expectCorrectTypes(context.multiTouchCancel(const <_TouchDetails>[ |
| _TouchDetails(pointer: 106, clientX: 500, clientY: 501), |
| _TouchDetails(pointer: 107, clientX: 502, clientY: 503), |
| ])); |
| expect(events.length, equals(2)); |
| expect(events[0].type, equals('pointercancel')); |
| expect(events[0].pointerId, equals(106)); |
| expect(events[0].button, equals(0)); |
| expect(events[0].buttons, equals(0)); |
| expect(events[0].client.x, equals(0)); |
| expect(events[0].client.y, equals(0)); |
| expect(events[1].type, equals('pointercancel')); |
| expect(events[1].pointerId, equals(107)); |
| expect(events[1].button, equals(0)); |
| expect(events[1].buttons, equals(0)); |
| expect(events[1].client.x, equals(0)); |
| expect(events[1].client.y, equals(0)); |
| }); |
| |
| test('_TouchEventContext generates expected events', () { |
| if (!_TouchEventContext().isSupported) { |
| return; |
| } |
| |
| html.TouchEvent expectCorrectType(html.Event e) { |
| expect(e.runtimeType, equals(html.TouchEvent)); |
| return e as html.TouchEvent; |
| } |
| |
| List<html.TouchEvent> expectCorrectTypes(List<html.Event> events) { |
| return events.map(expectCorrectType).toList(); |
| } |
| |
| final _TouchEventContext context = _TouchEventContext(); |
| html.TouchEvent event; |
| List<html.TouchEvent> events; |
| |
| event = expectCorrectType(context.primaryDown(clientX: 100, clientY: 101)); |
| expect(event.type, equals('touchstart')); |
| expect(event.changedTouches!.length, equals(1)); |
| expect(event.changedTouches![0].identifier, equals(1)); |
| expect(event.changedTouches![0].client.x, equals(100)); |
| expect(event.changedTouches![0].client.y, equals(101)); |
| |
| events = expectCorrectTypes(context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 100, clientX: 120, clientY: 121), |
| _TouchDetails(pointer: 101, clientX: 122, clientY: 123), |
| ])); |
| expect(events.length, equals(1)); |
| expect(events[0].type, equals('touchstart')); |
| expect(events[0].changedTouches!.length, equals(2)); |
| expect(events[0].changedTouches![0].identifier, equals(100)); |
| expect(events[0].changedTouches![0].client.x, equals(120)); |
| expect(events[0].changedTouches![0].client.y, equals(121)); |
| expect(events[0].changedTouches![1].identifier, equals(101)); |
| expect(events[0].changedTouches![1].client.x, equals(122)); |
| expect(events[0].changedTouches![1].client.y, equals(123)); |
| |
| event = expectCorrectType(context.primaryMove(clientX: 200, clientY: 201)); |
| expect(event.type, equals('touchmove')); |
| expect(event.changedTouches!.length, equals(1)); |
| expect(event.changedTouches![0].identifier, equals(1)); |
| expect(event.changedTouches![0].client.x, equals(200)); |
| expect(event.changedTouches![0].client.y, equals(201)); |
| |
| events = expectCorrectTypes(context.multiTouchMove(const <_TouchDetails>[ |
| _TouchDetails(pointer: 102, clientX: 220, clientY: 221), |
| _TouchDetails(pointer: 103, clientX: 222, clientY: 223), |
| ])); |
| expect(events.length, equals(1)); |
| expect(events[0].type, equals('touchmove')); |
| expect(events[0].changedTouches!.length, equals(2)); |
| expect(events[0].changedTouches![0].identifier, equals(102)); |
| expect(events[0].changedTouches![0].client.x, equals(220)); |
| expect(events[0].changedTouches![0].client.y, equals(221)); |
| expect(events[0].changedTouches![1].identifier, equals(103)); |
| expect(events[0].changedTouches![1].client.x, equals(222)); |
| expect(events[0].changedTouches![1].client.y, equals(223)); |
| |
| event = expectCorrectType(context.primaryUp(clientX: 300, clientY: 301)); |
| expect(event.type, equals('touchend')); |
| expect(event.changedTouches!.length, equals(1)); |
| expect(event.changedTouches![0].identifier, equals(1)); |
| expect(event.changedTouches![0].client.x, equals(300)); |
| expect(event.changedTouches![0].client.y, equals(301)); |
| |
| events = expectCorrectTypes(context.multiTouchUp(const <_TouchDetails>[ |
| _TouchDetails(pointer: 104, clientX: 320, clientY: 321), |
| _TouchDetails(pointer: 105, clientX: 322, clientY: 323), |
| ])); |
| expect(events.length, equals(1)); |
| expect(events[0].type, equals('touchend')); |
| expect(events[0].changedTouches!.length, equals(2)); |
| expect(events[0].changedTouches![0].identifier, equals(104)); |
| expect(events[0].changedTouches![0].client.x, equals(320)); |
| expect(events[0].changedTouches![0].client.y, equals(321)); |
| expect(events[0].changedTouches![1].identifier, equals(105)); |
| expect(events[0].changedTouches![1].client.x, equals(322)); |
| expect(events[0].changedTouches![1].client.y, equals(323)); |
| |
| events = expectCorrectTypes(context.multiTouchCancel(const <_TouchDetails>[ |
| _TouchDetails(pointer: 104, clientX: 320, clientY: 321), |
| _TouchDetails(pointer: 105, clientX: 322, clientY: 323), |
| ])); |
| expect(events.length, equals(1)); |
| expect(events[0].type, equals('touchcancel')); |
| expect(events[0].changedTouches!.length, equals(2)); |
| expect(events[0].changedTouches![0].identifier, equals(104)); |
| expect(events[0].changedTouches![0].client.x, equals(320)); |
| expect(events[0].changedTouches![0].client.y, equals(321)); |
| expect(events[0].changedTouches![1].identifier, equals(105)); |
| expect(events[0].changedTouches![1].client.x, equals(322)); |
| expect(events[0].changedTouches![1].client.y, equals(323)); |
| }); |
| |
| test('_MouseEventContext generates expected events', () { |
| if (!_MouseEventContext().isSupported) { |
| return; |
| } |
| |
| html.MouseEvent expectCorrectType(html.Event e) { |
| expect(e.runtimeType, equals(html.MouseEvent)); |
| return e as html.MouseEvent; |
| } |
| |
| final _MouseEventContext context = _MouseEventContext(); |
| html.MouseEvent event; |
| |
| event = expectCorrectType(context.primaryDown(clientX: 100, clientY: 101)); |
| expect(event.type, equals('mousedown')); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(100)); |
| expect(event.client.y, equals(101)); |
| |
| event = expectCorrectType( |
| context.mouseDown(clientX: 110, clientY: 111, button: 2, buttons: 2)); |
| expect(event.type, equals('mousedown')); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(2)); |
| expect(event.client.x, equals(110)); |
| expect(event.client.y, equals(111)); |
| |
| event = expectCorrectType(context.primaryMove(clientX: 200, clientY: 201)); |
| expect(event.type, equals('mousemove')); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(200)); |
| expect(event.client.y, equals(201)); |
| |
| event = expectCorrectType(context.mouseMove( |
| clientX: 210, clientY: 211, button: _kNoButtonChange, buttons: 6)); |
| expect(event.type, equals('mousemove')); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(6)); |
| expect(event.client.x, equals(210)); |
| expect(event.client.y, equals(211)); |
| |
| event = expectCorrectType( |
| context.mouseMove(clientX: 212, clientY: 213, button: 2, buttons: 6)); |
| expect(event.type, equals('mousedown')); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(6)); |
| expect(event.client.x, equals(212)); |
| expect(event.client.y, equals(213)); |
| |
| event = expectCorrectType( |
| context.mouseMove(clientX: 214, clientY: 215, button: 2, buttons: 1)); |
| expect(event.type, equals('mouseup')); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(1)); |
| expect(event.client.x, equals(214)); |
| expect(event.client.y, equals(215)); |
| |
| event = expectCorrectType(context.primaryUp(clientX: 300, clientY: 301)); |
| expect(event.type, equals('mouseup')); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(300)); |
| expect(event.client.y, equals(301)); |
| |
| event = expectCorrectType( |
| context.mouseUp(clientX: 310, clientY: 311, button: 2)); |
| expect(event.type, equals('mouseup')); |
| expect(event.button, equals(2)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(310)); |
| expect(event.client.y, equals(311)); |
| |
| event = expectCorrectType(context.hover(clientX: 400, clientY: 401)); |
| expect(event.type, equals('mousemove')); |
| expect(event.button, equals(0)); |
| expect(event.buttons, equals(0)); |
| expect(event.client.x, equals(400)); |
| expect(event.client.y, equals(401)); |
| }); |
| |
| // ALL ADAPTERS |
| |
| _testEach<_BasicEventContext>( |
| <_BasicEventContext>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| _TouchEventContext(), |
| ], |
| 'can receive pointer events on the glass pane', |
| (_BasicEventContext context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| ui.PointerDataPacket? receivedPacket; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| receivedPacket = packet; |
| }; |
| |
| glassPane.dispatchEvent(context.primaryDown()); |
| |
| expect(receivedPacket, isNotNull); |
| expect(receivedPacket!.data[0].buttons, equals(1)); |
| }, |
| ); |
| |
| _testEach<_BasicEventContext>( |
| <_BasicEventContext>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| _TouchEventContext(), |
| ], |
| 'does create an add event if got a pointerdown', |
| (_BasicEventContext context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.primaryDown()); |
| |
| expect(packets, hasLength(1)); |
| expect(packets.single.data, hasLength(2)); |
| |
| expect(packets.single.data[0].change, equals(ui.PointerChange.add)); |
| expect(packets.single.data[1].change, equals(ui.PointerChange.down)); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext(), |
| ], |
| 'correctly detects events on the semantics placeholder', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| final html.Element semanticsPlaceholder = |
| html.Element.tag('flt-semantics-placeholder'); |
| glassPane.append(semanticsPlaceholder); |
| |
| // Press on the semantics placeholder. |
| semanticsPlaceholder.dispatchEvent(context.primaryDown( |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| // Drag on the semantics placeholder. |
| semanticsPlaceholder.dispatchEvent(context.primaryMove( |
| clientX: 12.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].physicalX, equals(12.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| // Keep dragging. |
| semanticsPlaceholder.dispatchEvent(context.primaryMove( |
| clientX: 15.0, |
| clientY: 10.0, |
| )); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].physicalX, equals(15.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| // Release the pointer on the semantics placeholder. |
| html.window.dispatchEvent(context.primaryUp( |
| clientX: 100.0, |
| clientY: 200.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].physicalX, equals(100.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(200.0 * dpi)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[1].physicalX, equals(100.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(200.0 * dpi)); |
| packets.clear(); |
| |
| semanticsPlaceholder.remove(); |
| }, |
| ); |
| |
| // BUTTONED ADAPTERS |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'creates an add event if the first pointer activity is a hover', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.hover()); |
| |
| expect(packets, hasLength(1)); |
| expect(packets.single.data, hasLength(2)); |
| |
| expect(packets.single.data[0].change, equals(ui.PointerChange.add)); |
| expect(packets.single.data[0].synthesized, isTrue); |
| expect(packets.single.data[1].change, equals(ui.PointerChange.hover)); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'sends a pointermove event instead of the second pointerdown in a row', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.primaryDown( |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| // An add will be synthesized. |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.primaryDown( |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].buttons, equals(1)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext(), |
| ], |
| 'does synthesize add or hover or move for scroll', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.wheel( |
| buttons: 0, |
| clientX: 10, |
| clientY: 10, |
| deltaX: 10, |
| deltaY: 10, |
| )); |
| |
| glassPane.dispatchEvent(context.wheel( |
| buttons: 0, |
| clientX: 20, |
| clientY: 50, |
| deltaX: 10, |
| deltaY: 10, |
| )); |
| |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 0, |
| buttons: 1, |
| clientX: 20.0, |
| clientY: 50.0, |
| )); |
| |
| glassPane.dispatchEvent(context.wheel( |
| buttons: 1, |
| clientX: 30, |
| clientY: 60, |
| deltaX: 10, |
| deltaY: 10, |
| )); |
| |
| expect(packets, hasLength(4)); |
| |
| // An add will be synthesized. |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].pointerIdentifier, equals(0)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); |
| expect( |
| packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); |
| expect(packets[0].data[1].pointerIdentifier, equals(0)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0.0)); |
| |
| // A hover will be synthesized. |
| expect(packets[1].data, hasLength(2)); |
| expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); |
| expect(packets[1].data[0].pointerIdentifier, equals(0)); |
| expect(packets[1].data[0].synthesized, isTrue); |
| expect(packets[1].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[1].data[0].physicalY, equals(50.0 * dpi)); |
| expect(packets[1].data[0].physicalDeltaX, equals(10.0 * dpi)); |
| expect(packets[1].data[0].physicalDeltaY, equals(40.0 * dpi)); |
| |
| expect(packets[1].data[1].change, equals(ui.PointerChange.hover)); |
| expect( |
| packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); |
| expect(packets[1].data[1].pointerIdentifier, equals(0)); |
| expect(packets[1].data[1].synthesized, isFalse); |
| expect(packets[1].data[1].physicalX, equals(20.0 * dpi)); |
| expect(packets[1].data[1].physicalY, equals(50.0 * dpi)); |
| expect(packets[1].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[1].data[1].physicalDeltaY, equals(0.0)); |
| |
| // No synthetic pointer data for down event. |
| expect(packets[2].data, hasLength(1)); |
| expect(packets[2].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.none)); |
| expect(packets[2].data[0].pointerIdentifier, equals(1)); |
| expect(packets[2].data[0].synthesized, isFalse); |
| expect(packets[2].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[2].data[0].physicalY, equals(50.0 * dpi)); |
| expect(packets[2].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[2].data[0].physicalDeltaY, equals(0.0)); |
| |
| // A move will be synthesized instead of hover because the button is currently down. |
| expect(packets[3].data, hasLength(2)); |
| expect(packets[3].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[3].data[0].pointerIdentifier, equals(1)); |
| expect(packets[3].data[0].synthesized, isTrue); |
| expect(packets[3].data[0].physicalX, equals(30.0 * dpi)); |
| expect(packets[3].data[0].physicalY, equals(60.0 * dpi)); |
| expect(packets[3].data[0].physicalDeltaX, equals(10.0 * dpi)); |
| expect(packets[3].data[0].physicalDeltaY, equals(10.0 * dpi)); |
| |
| expect(packets[3].data[1].change, equals(ui.PointerChange.hover)); |
| expect( |
| packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); |
| expect(packets[3].data[1].pointerIdentifier, equals(1)); |
| expect(packets[3].data[1].synthesized, isFalse); |
| expect(packets[3].data[1].physicalX, equals(30.0 * dpi)); |
| expect(packets[3].data[1].physicalY, equals(60.0 * dpi)); |
| expect(packets[3].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[3].data[1].physicalDeltaY, equals(0.0)); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext() |
| ], |
| 'does calculate delta and pointer identifier correctly', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.hover( |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].pointerIdentifier, equals(0)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[1].pointerIdentifier, equals(0)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.hover( |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[0].pointerIdentifier, equals(0)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.primaryDown( |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.primaryMove( |
| clientX: 40.0, |
| clientY: 30.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.primaryUp( |
| clientX: 40.0, |
| clientY: 30.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.hover( |
| clientX: 20.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(-20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.primaryDown( |
| clientX: 20.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[0].pointerIdentifier, equals(2)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext(), |
| ], |
| 'correctly converts buttons of down, move and up events', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Add and hover |
| |
| glassPane.dispatchEvent(context.hover( |
| clientX: 10, |
| clientY: 11, |
| )); |
| |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(10 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(11 * dpi)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(10 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(11 * dpi)); |
| expect(packets[0].data[1].buttons, equals(0)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 0, |
| buttons: 1, |
| clientX: 10.0, |
| clientY: 11.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(10 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(11 * dpi)); |
| expect(packets[0].data[0].buttons, equals(1)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 1, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(1)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 0, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| |
| // Drag with secondary button |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(2)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 2, |
| clientX: 30.0, |
| clientY: 31.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(30 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(31 * dpi)); |
| expect(packets[0].data[0].buttons, equals(2)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 30.0, |
| clientY: 31.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(30 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(31 * dpi)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| |
| // Drag with middle button |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 1, |
| buttons: 4, |
| clientX: 30.0, |
| clientY: 31.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(30 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(31 * dpi)); |
| expect(packets[0].data[0].buttons, equals(4)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 4, |
| clientX: 40.0, |
| clientY: 41.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(41 * dpi)); |
| expect(packets[0].data[0].buttons, equals(4)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 1, |
| clientX: 40.0, |
| clientY: 41.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(41 * dpi)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles button changes during a down sequence', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press LMB. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 0, |
| buttons: 1, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(1)); |
| packets.clear(); |
| |
| // Press MMB. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: 1, |
| buttons: 5, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(5)); |
| packets.clear(); |
| |
| // Release LMB. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: 0, |
| buttons: 4, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(4)); |
| packets.clear(); |
| |
| // Release MMB. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 1, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext(), |
| ], |
| 'synthesizes a pointerup event when pointermove comes before the up', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen when the user pops up the context menu by right |
| // clicking, then dismisses it with a left click. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10, |
| clientY: 11, |
| )); |
| |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(10 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(11 * dpi)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(10 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(11 * dpi)); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 2, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(2)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 2, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(2)); |
| packets.clear(); |
| |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 20.0, |
| clientY: 21.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(20 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(21 * dpi)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles uncontinuous button changes during a down sequence', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking, but holds RMB; |
| // - Clicks LMB; |
| // - Releases RMB. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Press LMB. The event will have "button: -1" here, despite the change |
| // in "buttons", probably because the "press" gesture was absorbed by |
| // dismissing the context menu. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 3, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(3)); |
| packets.clear(); |
| |
| // Release LMB. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: 0, |
| buttons: 2, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(2)); |
| packets.clear(); |
| |
| // Release RMB. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| ], |
| 'correctly handles missing right mouse button up when followed by move', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking; |
| // - Clicks LMB to close context menu. |
| // - Moves mouse. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB popping up the context menu, then release by LMB down and up. |
| // Browser won't send up event in that case. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // User now hovers. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: _kNoButtonChange, |
| buttons: 0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'handles RMB click when the browser sends it as a move', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // When the user clicks the RMB and moves the mouse quickly (before the |
| // context menu shows up), the browser sends a move event before down. |
| // The move event will have "button:-1, buttons:2". |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseMove( |
| button: -1, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles hover after RMB click', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking, but holds RMB; |
| // - Move the pointer to hover. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Move the mouse. The event will have "buttons: 0" because RMB was |
| // released but the browser didn't send a pointerup/mouseup event. |
| // The hover is also triggered at a different position. |
| glassPane.dispatchEvent(context.hover( |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(3)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].buttons, equals(2)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(0)); |
| expect(packets[0].data[2].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[2].synthesized, isFalse); |
| expect(packets[0].data[2].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles LMB click after RMB click', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking, but holds RMB; |
| // - Clicks LMB in a different location; |
| // - Release LMB. |
| // |
| // The LMB click occurs in a different location because when RMB is |
| // clicked, and the contextmenu is shown, the browser stops sending |
| // `pointermove`/`mousemove` events. Then when the LMB click comes in, it |
| // could be in a different location without any `*move` events in between. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Press LMB. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 0, |
| buttons: 3, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(3)); |
| packets.clear(); |
| |
| // Release LMB. |
| glassPane.dispatchEvent(context.primaryUp( |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles two consecutive RMB clicks with no up in between', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking, but holds RMB; |
| // - Clicks RMB again in a different location; |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Press RMB again. In Chrome, when RMB is clicked again while the |
| // context menu is still active, it sends a pointerdown/mousedown event |
| // with "buttons:0". We convert this to pointer up, pointer down. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 0, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(3)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].buttons, equals(2)); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(0)); |
| expect(packets[0].data[2].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[2].synthesized, isFalse); |
| expect(packets[0].data[2].buttons, equals(2)); |
| packets.clear(); |
| |
| // Release RMB. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles two consecutive RMB clicks with up in between', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking, but doesn't hold RMB; |
| // - Clicks RMB again in a different location; |
| // |
| // This seems to be happening sometimes when using RMB on the Mac trackpad. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // RMB up. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| |
| // Press RMB again. In Chrome, when RMB is clicked again while the |
| // context menu is still active, it sends a pointerdown/mousedown event |
| // with "buttons:0". |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 0, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Release RMB. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'correctly handles two consecutive RMB clicks in two different locations', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // - Pops up the context menu by right clicking; |
| // - The browser sends RMB up event; |
| // - Click RMB again in a different location; |
| // |
| // This scenario happens occasionally. I'm still not sure why, but in some |
| // cases, the browser actually sends an `up` event for the RMB click even |
| // when the context menu is shown. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press RMB and hold, popping up the context menu. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| |
| // Release RMB. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| |
| // Press RMB again, in a different location. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 2, |
| clientX: 20.0, |
| clientY: 20.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(2)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| _PointerEventContext(), |
| _MouseEventContext(), |
| ], |
| 'handles overlapping left/right down and up events', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen with the following gesture sequence: |
| // |
| // LMB: down-------------------up |
| // RMB: down------------------up |
| // Flutter: down-------move-------move-------up |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press and hold LMB. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 0, |
| buttons: 1, |
| clientX: 5.0, |
| clientY: 100.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].buttons, equals(1)); |
| expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); |
| packets.clear(); |
| |
| // Press and hold RMB. The pointer is already down, so we only send a move |
| // to update the position of the pointer. |
| glassPane.dispatchEvent(context.mouseDown( |
| button: 2, |
| buttons: 3, |
| clientX: 20.0, |
| clientY: 100.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].buttons, equals(3)); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); |
| packets.clear(); |
| |
| // Release LMB. The pointer is still down (RMB), so we only send a move to |
| // update the position of the pointer. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 0, |
| buttons: 2, |
| clientX: 30.0, |
| clientY: 100.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].buttons, equals(2)); |
| expect(packets[0].data[0].physicalX, equals(30.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); |
| packets.clear(); |
| |
| // Release RMB. There's no more buttons down, so we send an up event. |
| glassPane.dispatchEvent(context.mouseUp( |
| button: 2, |
| buttons: 0, |
| clientX: 30.0, |
| clientY: 100.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_ButtonedEventMixin>( |
| <_ButtonedEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _MouseEventContext(), |
| ], |
| 'correctly detects up event outside of glasspane', |
| (_ButtonedEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This can happen when the up event occurs while the mouse is outside the |
| // browser window. |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Press and drag around. |
| glassPane.dispatchEvent(context.primaryDown( |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| glassPane.dispatchEvent(context.primaryMove( |
| clientX: 12.0, |
| clientY: 10.0, |
| )); |
| glassPane.dispatchEvent(context.primaryMove( |
| clientX: 15.0, |
| clientY: 10.0, |
| )); |
| glassPane.dispatchEvent(context.primaryMove( |
| clientX: 20.0, |
| clientY: 10.0, |
| )); |
| packets.clear(); |
| |
| // Move outside the glasspane. |
| html.window.dispatchEvent(context.primaryMove( |
| clientX: 900.0, |
| clientY: 1900.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].physicalX, |
| equals(900.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(1900.0 * dpi)); |
| packets.clear(); |
| |
| // Release outside the glasspane. |
| html.window.dispatchEvent(context.primaryUp( |
| clientX: 1000.0, |
| clientY: 2000.0, |
| )); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].physicalX, equals(1000.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(2000.0 * dpi)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[1].physicalX, equals(1000.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(2000.0 * dpi)); |
| packets.clear(); |
| }, |
| ); |
| |
| // MULTIPOINTER ADAPTERS |
| |
| _testEach<_MultiPointerEventMixin>( |
| <_MultiPointerEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _TouchEventContext(), |
| ], |
| 'treats each pointer separately', |
| (_MultiPointerEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| List<ui.PointerData> data; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Two pointers down |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 2, clientX: 100, clientY: 101), |
| _TouchDetails(pointer: 3, clientX: 200, clientY: 201), |
| ]).forEach(glassPane.dispatchEvent); |
| if (context.runtimeType == _PointerEventContext) { |
| expect(packets.length, 2); |
| expect(packets[0].data.length, 2); |
| expect(packets[1].data.length, 2); |
| } else if (context.runtimeType == _TouchEventContext) { |
| expect(packets.length, 1); |
| expect(packets[0].data.length, 4); |
| } else { |
| assert(false, 'Unexpected context type ${context.runtimeType}'); |
| } |
| |
| data = _allPointerData(packets); |
| expect(data, hasLength(4)); |
| expect(data[0].change, equals(ui.PointerChange.add)); |
| expect(data[0].synthesized, isTrue); |
| expect(data[0].device, equals(2)); |
| expect(data[0].physicalX, equals(100 * dpi)); |
| expect(data[0].physicalY, equals(101 * dpi)); |
| |
| expect(data[1].change, equals(ui.PointerChange.down)); |
| expect(data[1].device, equals(2)); |
| expect(data[1].buttons, equals(1)); |
| expect(data[1].physicalX, equals(100 * dpi)); |
| expect(data[1].physicalY, equals(101 * dpi)); |
| expect(data[1].physicalDeltaX, equals(0)); |
| expect(data[1].physicalDeltaY, equals(0)); |
| |
| expect(data[2].change, equals(ui.PointerChange.add)); |
| expect(data[2].synthesized, isTrue); |
| expect(data[2].device, equals(3)); |
| expect(data[2].physicalX, equals(200 * dpi)); |
| expect(data[2].physicalY, equals(201 * dpi)); |
| |
| expect(data[3].change, equals(ui.PointerChange.down)); |
| expect(data[3].device, equals(3)); |
| expect(data[3].buttons, equals(1)); |
| expect(data[3].physicalX, equals(200 * dpi)); |
| expect(data[3].physicalY, equals(201 * dpi)); |
| expect(data[3].physicalDeltaX, equals(0)); |
| expect(data[3].physicalDeltaY, equals(0)); |
| packets.clear(); |
| |
| // Two pointers move |
| context.multiTouchMove(const <_TouchDetails>[ |
| _TouchDetails(pointer: 3, clientX: 300, clientY: 302), |
| _TouchDetails(pointer: 2, clientX: 400, clientY: 402), |
| ]).forEach(glassPane.dispatchEvent); |
| if (context.runtimeType == _PointerEventContext) { |
| expect(packets.length, 2); |
| expect(packets[0].data.length, 1); |
| expect(packets[1].data.length, 1); |
| } else if (context.runtimeType == _TouchEventContext) { |
| expect(packets.length, 1); |
| expect(packets[0].data.length, 2); |
| } else { |
| assert(false, 'Unexpected context type ${context.runtimeType}'); |
| } |
| |
| data = _allPointerData(packets); |
| expect(data, hasLength(2)); |
| expect(data[0].change, equals(ui.PointerChange.move)); |
| expect(data[0].device, equals(3)); |
| expect(data[0].buttons, equals(1)); |
| expect(data[0].physicalX, equals(300 * dpi)); |
| expect(data[0].physicalY, equals(302 * dpi)); |
| expect(data[0].physicalDeltaX, equals(100 * dpi)); |
| expect(data[0].physicalDeltaY, equals(101 * dpi)); |
| |
| expect(data[1].change, equals(ui.PointerChange.move)); |
| expect(data[1].device, equals(2)); |
| expect(data[1].buttons, equals(1)); |
| expect(data[1].physicalX, equals(400 * dpi)); |
| expect(data[1].physicalY, equals(402 * dpi)); |
| expect(data[1].physicalDeltaX, equals(300 * dpi)); |
| expect(data[1].physicalDeltaY, equals(301 * dpi)); |
| packets.clear(); |
| |
| // One pointer up |
| context.multiTouchUp(const <_TouchDetails>[ |
| _TouchDetails(pointer: 3, clientX: 300, clientY: 302), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].device, equals(3)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[0].physicalX, equals(300 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(302 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); |
| expect(packets[0].data[1].device, equals(3)); |
| expect(packets[0].data[1].buttons, equals(0)); |
| expect(packets[0].data[1].physicalX, equals(300 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(302 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0)); |
| packets.clear(); |
| |
| // Another pointer up |
| context.multiTouchUp(const <_TouchDetails>[ |
| _TouchDetails(pointer: 2, clientX: 400, clientY: 402), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].device, equals(2)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[0].physicalX, equals(400 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(402 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); |
| expect(packets[0].data[1].device, equals(2)); |
| expect(packets[0].data[1].buttons, equals(0)); |
| expect(packets[0].data[1].physicalX, equals(400 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(402 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0)); |
| packets.clear(); |
| |
| // Again two pointers down (reuse pointer ID) |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 3, clientX: 500, clientY: 501), |
| _TouchDetails(pointer: 2, clientX: 600, clientY: 601), |
| ]).forEach(glassPane.dispatchEvent); |
| if (context.runtimeType == _PointerEventContext) { |
| expect(packets.length, 2); |
| expect(packets[0].data.length, 2); |
| expect(packets[1].data.length, 2); |
| } else if (context.runtimeType == _TouchEventContext) { |
| expect(packets.length, 1); |
| expect(packets[0].data.length, 4); |
| } else { |
| assert(false, 'Unexpected context type ${context.runtimeType}'); |
| } |
| |
| data = _allPointerData(packets); |
| expect(data, hasLength(4)); |
| expect(data[0].change, equals(ui.PointerChange.add)); |
| expect(data[0].synthesized, isTrue); |
| expect(data[0].device, equals(3)); |
| expect(data[0].physicalX, equals(500 * dpi)); |
| expect(data[0].physicalY, equals(501 * dpi)); |
| |
| expect(data[1].change, equals(ui.PointerChange.down)); |
| expect(data[1].device, equals(3)); |
| expect(data[1].buttons, equals(1)); |
| expect(data[1].physicalX, equals(500 * dpi)); |
| expect(data[1].physicalY, equals(501 * dpi)); |
| expect(data[1].physicalDeltaX, equals(0)); |
| expect(data[1].physicalDeltaY, equals(0)); |
| |
| expect(data[2].change, equals(ui.PointerChange.add)); |
| expect(data[2].synthesized, isTrue); |
| expect(data[2].device, equals(2)); |
| expect(data[2].physicalX, equals(600 * dpi)); |
| expect(data[2].physicalY, equals(601 * dpi)); |
| |
| expect(data[3].change, equals(ui.PointerChange.down)); |
| expect(data[3].device, equals(2)); |
| expect(data[3].buttons, equals(1)); |
| expect(data[3].physicalX, equals(600 * dpi)); |
| expect(data[3].physicalY, equals(601 * dpi)); |
| expect(data[3].physicalDeltaX, equals(0)); |
| expect(data[3].physicalDeltaY, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_MultiPointerEventMixin>( |
| <_MultiPointerEventMixin>[ |
| if (!isIosSafari) _PointerEventContext(), |
| if (!isIosSafari) _TouchEventContext(), |
| ], |
| 'correctly parses cancel event', |
| (_MultiPointerEventMixin context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| // Two pointers down |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 2, clientX: 100, clientY: 101), |
| _TouchDetails(pointer: 3, clientX: 200, clientY: 201), |
| ]).forEach(glassPane.dispatchEvent); |
| packets.clear(); // Down event is tested in other tests. |
| |
| // One pointer cancel |
| context.multiTouchCancel(const <_TouchDetails>[ |
| _TouchDetails(pointer: 3, clientX: 300, clientY: 302), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets.length, 1); |
| expect(packets[0].data.length, 2); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.cancel)); |
| expect(packets[0].data[0].device, equals(3)); |
| expect(packets[0].data[0].buttons, equals(0)); |
| expect(packets[0].data[0].physicalX, equals(200 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(201 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); |
| expect(packets[0].data[1].device, equals(3)); |
| expect(packets[0].data[1].buttons, equals(0)); |
| expect(packets[0].data[1].physicalX, equals(200 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(201 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0)); |
| packets.clear(); |
| }, |
| ); |
| |
| // POINTER ADAPTER |
| |
| _testEach<_PointerEventContext>( |
| <_PointerEventContext>[ |
| if (!isIosSafari) _PointerEventContext(), |
| ], |
| 'does not synthesize pointer up if from different device', |
| (_PointerEventContext context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 1, clientX: 100, clientY: 101), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| // An add will be synthesized. |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].device, equals(1)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].device, equals(1)); |
| packets.clear(); |
| |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 2, clientX: 200, clientY: 202), |
| ]).forEach(glassPane.dispatchEvent); |
| // An add will be synthesized. |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].device, equals(2)); |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].device, equals(2)); |
| packets.clear(); |
| }, |
| ); |
| |
| _testEach<_PointerEventContext>( |
| <_PointerEventContext>[ |
| _PointerEventContext(), |
| ], |
| 'handles random pointer id on up events', |
| (_PointerEventContext context) { |
| PointerBinding.instance!.debugOverrideDetector(context); |
| // This happens with pens that are simulated with mouse events |
| // (e.g. Wacom). It sends events with the pointer type "mouse", and |
| // assigns a random pointer ID to each event. |
| // |
| // For more info, see: https://github.com/flutter/flutter/issues/75559 |
| |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| glassPane.dispatchEvent(context.mouseDown( |
| pointerId: 12, |
| button: 0, |
| buttons: 1, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| |
| expect(packets, hasLength(1)); |
| expect(packets.single.data, hasLength(2)); |
| |
| expect(packets.single.data[0].change, equals(ui.PointerChange.add)); |
| expect(packets.single.data[0].synthesized, isTrue); |
| expect(packets.single.data[1].change, equals(ui.PointerChange.down)); |
| packets.clear(); |
| |
| expect( |
| () { |
| glassPane.dispatchEvent(context.mouseUp( |
| pointerId: 41, |
| button: 0, |
| buttons: 0, |
| clientX: 10.0, |
| clientY: 10.0, |
| )); |
| }, |
| returnsNormally, |
| ); |
| |
| expect(packets, hasLength(1)); |
| expect(packets.single.data, hasLength(1)); |
| |
| expect(packets.single.data[0].change, equals(ui.PointerChange.up)); |
| }, |
| ); |
| |
| // TOUCH ADAPTER |
| |
| _testEach<_TouchEventContext>( |
| <_TouchEventContext>[ |
| if (!isIosSafari) _TouchEventContext(), |
| ], |
| 'does calculate delta and pointer identifier correctly', |
| (_TouchEventContext context) { |
| // Mouse and Pointer are in another test since these tests can involve hovering |
| PointerBinding.instance!.debugOverrideDetector(context); |
| final List<ui.PointerDataPacket> packets = <ui.PointerDataPacket>[]; |
| ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { |
| packets.add(packet); |
| }; |
| |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 1, clientX: 20, clientY: 20), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].pointerIdentifier, equals(1)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(20.0 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| |
| context.multiTouchMove(const <_TouchDetails>[ |
| _TouchDetails(pointer: 1, clientX: 40, clientY: 30), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(1)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.move)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); |
| packets.clear(); |
| |
| context.multiTouchUp(const <_TouchDetails>[ |
| _TouchDetails(pointer: 1, clientX: 40, clientY: 30), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.up)); |
| expect(packets[0].data[0].pointerIdentifier, equals(1)); |
| expect(packets[0].data[0].synthesized, isFalse); |
| expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); |
| expect(packets[0].data[1].pointerIdentifier, equals(1)); |
| expect(packets[0].data[1].synthesized, isTrue); |
| expect(packets[0].data[1].physicalX, equals(40.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(30.0 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| |
| context.multiTouchDown(const <_TouchDetails>[ |
| _TouchDetails(pointer: 2, clientX: 20, clientY: 10), |
| ]).forEach(glassPane.dispatchEvent); |
| expect(packets, hasLength(1)); |
| expect(packets[0].data, hasLength(2)); |
| expect(packets[0].data[0].change, equals(ui.PointerChange.add)); |
| expect(packets[0].data[0].pointerIdentifier, equals(2)); |
| expect(packets[0].data[0].synthesized, isTrue); |
| expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[0].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[0].physicalDeltaY, equals(0.0)); |
| |
| expect(packets[0].data[1].change, equals(ui.PointerChange.down)); |
| expect(packets[0].data[1].pointerIdentifier, equals(2)); |
| expect(packets[0].data[1].synthesized, isFalse); |
| expect(packets[0].data[1].physicalX, equals(20.0 * dpi)); |
| expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); |
| expect(packets[0].data[1].physicalDeltaX, equals(0.0)); |
| expect(packets[0].data[1].physicalDeltaY, equals(0.0)); |
| packets.clear(); |
| }, |
| ); |
| } |
| |
| abstract class _BasicEventContext implements PointerSupportDetector { |
| String get name; |
| |
| bool get isSupported; |
| |
| // Generate an event that is: |
| // |
| // * For mouse, a left click |
| // * For touch, a touch down |
| html.Event primaryDown({double clientX, double clientY}); |
| |
| // Generate an event that is: |
| // |
| // * For mouse, a drag with LMB down |
| // * For touch, a touch drag |
| html.Event primaryMove({double clientX, double clientY}); |
| |
| // Generate an event that is: |
| // |
| // * For mouse, release LMB |
| // * For touch, a touch up |
| html.Event primaryUp({double clientX, double clientY}); |
| } |
| |
| mixin _ButtonedEventMixin on _BasicEventContext { |
| // Generate an event that is a mouse down with the specific buttons. |
| html.Event mouseDown( |
| {double? clientX, double? clientY, int? button, int? buttons}); |
| |
| // Generate an event that is a mouse drag with the specific buttons, or button |
| // changes during the drag. |
| // |
| // If there is no button change, assign `button` with _kNoButtonChange. |
| html.Event mouseMove( |
| {double? clientX, double? clientY, required int button, required int buttons}); |
| |
| // Generate an event that releases all mouse buttons. |
| html.Event mouseUp({double? clientX, double? clientY, int? button, int? buttons}); |
| |
| html.Event hover({double? clientX, double? clientY}) { |
| return mouseMove( |
| buttons: 0, |
| button: _kNoButtonChange, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| @override |
| html.Event primaryDown({double? clientX, double? clientY}) { |
| return mouseDown( |
| buttons: 1, |
| button: 0, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| @override |
| html.Event primaryMove({double? clientX, double? clientY}) { |
| return mouseMove( |
| buttons: 1, |
| button: _kNoButtonChange, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| @override |
| html.Event primaryUp({double? clientX, double? clientY}) { |
| return mouseUp( |
| button: 0, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| html.Event wheel({ |
| required int? buttons, |
| required double? clientX, |
| required double? clientY, |
| required double? deltaX, |
| required double? deltaY, |
| }) { |
| // ignore: implicit_dynamic_function |
| final Function jsWheelEvent = js_util.getProperty(html.window, 'WheelEvent') as Function; |
| final List<dynamic> eventArgs = <dynamic>[ |
| 'wheel', |
| <String, dynamic>{ |
| 'buttons': buttons, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| 'deltaX': deltaX, |
| 'deltaY': deltaY, |
| } |
| ]; |
| // ignore: implicit_dynamic_function |
| return js_util.callConstructor( |
| jsWheelEvent, |
| js_util.jsify(eventArgs) as List<Object?>, |
| ) as html.Event; |
| } |
| } |
| |
| class _TouchDetails { |
| const _TouchDetails({this.pointer, this.clientX, this.clientY}); |
| |
| final int? pointer; |
| final double? clientX; |
| final double? clientY; |
| } |
| |
| mixin _MultiPointerEventMixin on _BasicEventContext { |
| List<html.Event> multiTouchDown(List<_TouchDetails> touches); |
| List<html.Event> multiTouchMove(List<_TouchDetails> touches); |
| List<html.Event> multiTouchUp(List<_TouchDetails> touches); |
| List<html.Event> multiTouchCancel(List<_TouchDetails> touches); |
| |
| @override |
| html.Event primaryDown({double? clientX, double? clientY}) { |
| return multiTouchDown(<_TouchDetails>[ |
| _TouchDetails( |
| pointer: 1, |
| clientX: clientX, |
| clientY: clientY, |
| ), |
| ])[0]; |
| } |
| |
| @override |
| html.Event primaryMove({double? clientX, double? clientY}) { |
| return multiTouchMove(<_TouchDetails>[ |
| _TouchDetails( |
| pointer: 1, |
| clientX: clientX, |
| clientY: clientY, |
| ), |
| ])[0]; |
| } |
| |
| @override |
| html.Event primaryUp({double? clientX, double? clientY}) { |
| return multiTouchUp(<_TouchDetails>[ |
| _TouchDetails( |
| pointer: 1, |
| clientX: clientX, |
| clientY: clientY, |
| ), |
| ])[0]; |
| } |
| } |
| |
| // A test context for `_TouchAdapter`, including its name, PointerSupportDetector |
| // to override, and how to generate events. |
| class _TouchEventContext extends _BasicEventContext |
| with _MultiPointerEventMixin |
| implements PointerSupportDetector { |
| _TouchEventContext() : |
| _target = html.document.createElement('div'); |
| |
| @override |
| String get name => 'TouchAdapter'; |
| |
| @override |
| bool get isSupported => _defaultSupportDetector.hasTouchEvents; |
| |
| @override |
| bool get hasPointerEvents => false; |
| |
| @override |
| bool get hasTouchEvents => true; |
| |
| @override |
| bool get hasMouseEvents => false; |
| |
| html.EventTarget _target; |
| |
| html.Touch _createTouch({ |
| int? identifier, |
| double? clientX, |
| double? clientY, |
| }) { |
| return html.Touch(<String, dynamic>{ |
| 'identifier': identifier, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| 'target': _target, |
| }); |
| } |
| |
| html.TouchEvent _createTouchEvent( |
| String eventType, List<_TouchDetails> touches) { |
| return html.TouchEvent( |
| eventType, |
| <String, dynamic>{ |
| 'changedTouches': touches |
| .map( |
| (_TouchDetails details) => _createTouch( |
| identifier: details.pointer, |
| clientX: details.clientX, |
| clientY: details.clientY, |
| ), |
| ) |
| .toList(), |
| }, |
| ); |
| } |
| |
| @override |
| List<html.Event> multiTouchDown(List<_TouchDetails> touches) { |
| return <html.Event>[_createTouchEvent('touchstart', touches)]; |
| } |
| |
| @override |
| List<html.Event> multiTouchMove(List<_TouchDetails> touches) { |
| return <html.Event>[_createTouchEvent('touchmove', touches)]; |
| } |
| |
| @override |
| List<html.Event> multiTouchUp(List<_TouchDetails> touches) { |
| return <html.Event>[_createTouchEvent('touchend', touches)]; |
| } |
| |
| @override |
| List<html.Event> multiTouchCancel(List<_TouchDetails> touches) { |
| return <html.Event>[_createTouchEvent('touchcancel', touches)]; |
| } |
| } |
| |
| // A test context for `_MouseAdapter`, including its name, PointerSupportDetector |
| // to override, and how to generate events. |
| // |
| // For the difference between MouseEvent and PointerEvent, see _MouseAdapter. |
| class _MouseEventContext extends _BasicEventContext |
| with _ButtonedEventMixin |
| implements PointerSupportDetector { |
| @override |
| String get name => 'MouseAdapter'; |
| |
| @override |
| bool get isSupported => _defaultSupportDetector.hasMouseEvents; |
| |
| @override |
| bool get hasPointerEvents => false; |
| |
| @override |
| bool get hasTouchEvents => false; |
| |
| @override |
| bool get hasMouseEvents => true; |
| |
| @override |
| html.Event mouseDown({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| }) { |
| return _createMouseEvent( |
| 'mousedown', |
| buttons: buttons, |
| button: button, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| @override |
| html.Event mouseMove({ |
| double? clientX, |
| double? clientY, |
| required int button, |
| required int buttons, |
| }) { |
| final bool hasButtonChange = button != _kNoButtonChange; |
| final bool changeIsButtonDown = |
| hasButtonChange && (buttons & convertButtonToButtons(button)) != 0; |
| final String adjustedType = !hasButtonChange |
| ? 'mousemove' |
| : changeIsButtonDown ? 'mousedown' : 'mouseup'; |
| final int adjustedButton = hasButtonChange ? button : 0; |
| return _createMouseEvent( |
| adjustedType, |
| buttons: buttons, |
| button: adjustedButton, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| @override |
| html.Event mouseUp({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| }) { |
| return _createMouseEvent( |
| 'mouseup', |
| buttons: buttons, |
| button: button, |
| clientX: clientX, |
| clientY: clientY, |
| ); |
| } |
| |
| html.MouseEvent _createMouseEvent( |
| String type, { |
| int? buttons, |
| int? button, |
| double? clientX, |
| double? clientY, |
| }) { |
| final Function jsMouseEvent = |
| // ignore: implicit_dynamic_function |
| js_util.getProperty(html.window, 'MouseEvent') as Function; |
| final List<dynamic> eventArgs = <dynamic>[ |
| type, |
| <String, dynamic>{ |
| 'buttons': buttons, |
| 'button': button, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| } |
| ]; |
| // ignore: implicit_dynamic_function |
| return js_util.callConstructor( |
| jsMouseEvent, |
| js_util.jsify(eventArgs) as List<Object?>, |
| ) as html.MouseEvent; |
| } |
| } |
| |
| // A test context for `_PointerAdapter`, including its name, PointerSupportDetector |
| // to override, and how to generate events. |
| // |
| // For the difference between MouseEvent and PointerEvent, see _MouseAdapter. |
| class _PointerEventContext extends _BasicEventContext |
| with _ButtonedEventMixin |
| implements PointerSupportDetector, _MultiPointerEventMixin { |
| @override |
| String get name => 'PointerAdapter'; |
| |
| @override |
| bool get isSupported => _defaultSupportDetector.hasPointerEvents; |
| |
| @override |
| bool get hasPointerEvents => true; |
| |
| @override |
| bool get hasTouchEvents => false; |
| |
| @override |
| bool get hasMouseEvents => false; |
| |
| @override |
| List<html.Event> multiTouchDown(List<_TouchDetails> touches) { |
| return touches |
| .map((_TouchDetails details) => _downWithFullDetails( |
| pointer: details.pointer, |
| buttons: 1, |
| button: 0, |
| clientX: details.clientX, |
| clientY: details.clientY, |
| pointerType: 'touch', |
| )) |
| .toList(); |
| } |
| |
| @override |
| html.Event mouseDown({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| int? pointerId = 1, |
| }) { |
| return _downWithFullDetails( |
| pointer: pointerId, |
| buttons: buttons, |
| button: button, |
| clientX: clientX, |
| clientY: clientY, |
| pointerType: 'mouse', |
| ); |
| } |
| |
| html.Event _downWithFullDetails({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| int? pointer, |
| String? pointerType, |
| }) { |
| return html.PointerEvent('pointerdown', <String, dynamic>{ |
| 'pointerId': pointer, |
| 'button': button, |
| 'buttons': buttons, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| 'pointerType': pointerType, |
| }); |
| } |
| |
| @override |
| List<html.Event> multiTouchMove(List<_TouchDetails> touches) { |
| return touches |
| .map((_TouchDetails details) => _moveWithFullDetails( |
| pointer: details.pointer, |
| buttons: 1, |
| button: _kNoButtonChange, |
| clientX: details.clientX, |
| clientY: details.clientY, |
| pointerType: 'touch', |
| )) |
| .toList(); |
| } |
| |
| @override |
| html.Event mouseMove({ |
| double? clientX, |
| double? clientY, |
| required int button, |
| required int buttons, |
| int pointerId = 1, |
| }) { |
| return _moveWithFullDetails( |
| pointer: pointerId, |
| buttons: buttons, |
| button: button, |
| clientX: clientX, |
| clientY: clientY, |
| pointerType: 'mouse', |
| ); |
| } |
| |
| html.Event _moveWithFullDetails({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| int? pointer, |
| String? pointerType, |
| }) { |
| return html.PointerEvent('pointermove', <String, dynamic>{ |
| 'pointerId': pointer, |
| 'button': button, |
| 'buttons': buttons, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| 'pointerType': pointerType, |
| }); |
| } |
| |
| @override |
| List<html.Event> multiTouchUp(List<_TouchDetails> touches) { |
| return touches |
| .map((_TouchDetails details) => _upWithFullDetails( |
| pointer: details.pointer, |
| button: 0, |
| clientX: details.clientX, |
| clientY: details.clientY, |
| pointerType: 'touch', |
| )) |
| .toList(); |
| } |
| |
| @override |
| html.Event mouseUp({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| int? pointerId = 1, |
| }) { |
| return _upWithFullDetails( |
| pointer: pointerId, |
| button: button, |
| buttons: buttons, |
| clientX: clientX, |
| clientY: clientY, |
| pointerType: 'mouse', |
| ); |
| } |
| |
| html.Event _upWithFullDetails({ |
| double? clientX, |
| double? clientY, |
| int? button, |
| int? buttons, |
| int? pointer, |
| String? pointerType, |
| }) { |
| return html.PointerEvent('pointerup', <String, dynamic>{ |
| 'pointerId': pointer, |
| 'button': button, |
| 'buttons': buttons, |
| 'clientX': clientX, |
| 'clientY': clientY, |
| 'pointerType': pointerType, |
| }); |
| } |
| |
| @override |
| List<html.Event> multiTouchCancel(List<_TouchDetails> touches) { |
| return touches |
| .map((_TouchDetails details) => |
| html.PointerEvent('pointercancel', <String, dynamic>{ |
| 'pointerId': details.pointer, |
| 'button': 0, |
| 'buttons': 0, |
| 'clientX': 0, |
| 'clientY': 0, |
| 'pointerType': 'touch', |
| })) |
| .toList(); |
| } |
| } |