gesture_binding_test.dart 14.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:ui' as ui;

7
import 'package:flutter/foundation.dart';
8
import 'package:flutter/gestures.dart';
9
import 'package:flutter/scheduler.dart';
10
import 'package:flutter_test/flutter_test.dart';
11

12
typedef HandleEventCallback = void Function(PointerEvent event);
13

14
class TestGestureFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding {
15
  HandleEventCallback? callback;
16 17 18

  @override
  void handleEvent(PointerEvent event, HitTestEntry entry) {
19
    super.handleEvent(event, entry);
20
    if (callback != null)
21
      callback?.call(event);
22
  }
23 24
}

25 26 27 28 29 30 31 32
TestGestureFlutterBinding? _binding;

void ensureTestGestureBinding() {
  _binding ??= TestGestureFlutterBinding();
  assert(GestureBinding.instance != null);
  assert(SchedulerBinding.instance != null);
}

33
void main() {
34
  setUp(ensureTestGestureBinding);
35 36

  test('Pointer tap events', () {
37 38 39 40
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.down),
        ui.PointerData(change: ui.PointerChange.up),
41
      ],
42
    );
43

44
    final List<PointerEvent> events = <PointerEvent>[];
45
    _binding!.callback = events.add;
46

47
    ui.window.onPointerDataPacket?.call(packet);
48
    expect(events.length, 2);
49 50
    expect(events[0], isA<PointerDownEvent>());
    expect(events[1], isA<PointerUpEvent>());
51 52
  });

53
  test('Pointer move events', () {
54 55 56 57 58
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.down),
        ui.PointerData(change: ui.PointerChange.move),
        ui.PointerData(change: ui.PointerChange.up),
59
      ],
60
    );
61

62
    final List<PointerEvent> events = <PointerEvent>[];
63
    _binding!.callback = events.add;
64

65
    ui.window.onPointerDataPacket?.call(packet);
66
    expect(events.length, 3);
67 68 69
    expect(events[0], isA<PointerDownEvent>());
    expect(events[1], isA<PointerMoveEvent>());
    expect(events[2], isA<PointerUpEvent>());
70 71
  });

72 73 74
  test('Pointer hover events', () {
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
        data: <ui.PointerData>[
75
          ui.PointerData(change: ui.PointerChange.add),
76 77 78
          ui.PointerData(change: ui.PointerChange.hover),
          ui.PointerData(change: ui.PointerChange.hover),
          ui.PointerData(change: ui.PointerChange.remove),
79
          ui.PointerData(change: ui.PointerChange.add),
80
          ui.PointerData(change: ui.PointerChange.hover),
81
        ],
82 83 84
    );

    final List<PointerEvent> pointerRouterEvents = <PointerEvent>[];
85
    GestureBinding.instance!.pointerRouter.addGlobalRoute(pointerRouterEvents.add);
86 87

    final List<PointerEvent> events = <PointerEvent>[];
88
    _binding!.callback = events.add;
89

90
    ui.window.onPointerDataPacket?.call(packet);
91 92 93 94
    expect(events.length, 3);
    expect(events[0], isA<PointerHoverEvent>());
    expect(events[1], isA<PointerHoverEvent>());
    expect(events[2], isA<PointerHoverEvent>());
95
    expect(pointerRouterEvents.length, 6, reason: 'pointerRouterEvents contains: $pointerRouterEvents');
96 97 98 99 100 101
    expect(pointerRouterEvents[0], isA<PointerAddedEvent>());
    expect(pointerRouterEvents[1], isA<PointerHoverEvent>());
    expect(pointerRouterEvents[2], isA<PointerHoverEvent>());
    expect(pointerRouterEvents[3], isA<PointerRemovedEvent>());
    expect(pointerRouterEvents[4], isA<PointerAddedEvent>());
    expect(pointerRouterEvents[5], isA<PointerHoverEvent>());
102 103
  });

104
  test('Pointer cancel events', () {
105 106 107 108
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.down),
        ui.PointerData(change: ui.PointerChange.cancel),
109
      ],
110
    );
111

112
    final List<PointerEvent> events = <PointerEvent>[];
113
    _binding!.callback = events.add;
114

115
    ui.window.onPointerDataPacket?.call(packet);
116
    expect(events.length, 2);
117 118
    expect(events[0], isA<PointerDownEvent>());
    expect(events[1], isA<PointerCancelEvent>());
119 120 121
  });

  test('Can cancel pointers', () {
122 123 124 125
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.down),
        ui.PointerData(change: ui.PointerChange.up),
126
      ],
127
    );
128

129
    final List<PointerEvent> events = <PointerEvent>[];
130
    _binding!.callback = (PointerEvent event) {
131 132
      events.add(event);
      if (event is PointerDownEvent)
133
        _binding!.cancelPointer(event.pointer);
134 135
    };

136
    ui.window.onPointerDataPacket?.call(packet);
137
    expect(events.length, 2);
138 139
    expect(events[0], isA<PointerDownEvent>());
    expect(events[1], isA<PointerCancelEvent>());
140
  });
141 142

  test('Can expand add and hover pointers', () {
143 144 145 146 147
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.add, device: 24),
        ui.PointerData(change: ui.PointerChange.hover, device: 24),
        ui.PointerData(change: ui.PointerChange.remove, device: 24),
148
        ui.PointerData(change: ui.PointerChange.add, device: 24),
149
        ui.PointerData(change: ui.PointerChange.hover, device: 24),
150
      ],
151 152
    );

153
    final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
154 155

    expect(events.length, 5);
156 157 158 159 160
    expect(events[0], isA<PointerAddedEvent>());
    expect(events[1], isA<PointerHoverEvent>());
    expect(events[2], isA<PointerRemovedEvent>());
    expect(events[3], isA<PointerAddedEvent>());
    expect(events[4], isA<PointerHoverEvent>());
161 162
  });

163 164 165 166 167
  test('Can expand pointer scroll events', () {
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
        data: <ui.PointerData>[
          ui.PointerData(change: ui.PointerChange.add),
          ui.PointerData(change: ui.PointerChange.hover, signalKind: ui.PointerSignalKind.scroll),
168
        ],
169 170
    );

171
    final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
172 173

    expect(events.length, 2);
174 175
    expect(events[0], isA<PointerAddedEvent>());
    expect(events[1], isA<PointerScrollEvent>());
176 177
  });

178
  test('Should synthesize kPrimaryButton for touch when no button is set', () {
179 180 181 182 183 184 185 186 187
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
    const PointerDeviceKind kind = PointerDeviceKind.touch;
    final ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.down, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.move, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
188
      ],
189 190
    );

191
    final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
192 193

    expect(events.length, 5);
194
    expect(events[0], isA<PointerAddedEvent>());
195
    expect(events[0].buttons, equals(0));
196
    expect(events[1], isA<PointerHoverEvent>());
197
    expect(events[1].buttons, equals(0));
198
    expect(events[2], isA<PointerDownEvent>());
199
    expect(events[2].buttons, equals(kPrimaryButton));
200
    expect(events[3], isA<PointerMoveEvent>());
201
    expect(events[3].buttons, equals(kPrimaryButton));
202
    expect(events[4], isA<PointerUpEvent>());
203 204 205
    expect(events[4].buttons, equals(0));
  });

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
  test('Should not synthesize kPrimaryButton for touch when a button is set', () {
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
    const PointerDeviceKind kind = PointerDeviceKind.touch;
    final ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.down, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
      ],
    );

    final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();

    expect(events.length, 5);
    expect(events[0], isA<PointerAddedEvent>());
    expect(events[0].buttons, equals(0));
    expect(events[1], isA<PointerHoverEvent>());
    expect(events[1].buttons, equals(0));
    expect(events[2], isA<PointerDownEvent>());
    expect(events[2].buttons, equals(kSecondaryButton));
    expect(events[3], isA<PointerMoveEvent>());
    expect(events[3].buttons, equals(kSecondaryButton));
    expect(events[4], isA<PointerUpEvent>());
    expect(events[4].buttons, equals(0));
  });

  test('Should synthesize kPrimaryButton for stylus when no button is set', () {
235
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
236
    for (final PointerDeviceKind kind in <PointerDeviceKind>[
237 238 239 240 241 242 243 244 245 246 247
      PointerDeviceKind.stylus,
      PointerDeviceKind.invertedStylus,
    ]) {

      final ui.PointerDataPacket packet = ui.PointerDataPacket(
        data: <ui.PointerData>[
          ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.down, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryStylusButton, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
248
        ],
249 250
      );

251
      final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
252 253

      expect(events.length, 5);
254
      expect(events[0], isA<PointerAddedEvent>());
255
      expect(events[0].buttons, equals(0));
256
      expect(events[1], isA<PointerHoverEvent>());
257
      expect(events[1].buttons, equals(0));
258
      expect(events[2], isA<PointerDownEvent>());
259
      expect(events[2].buttons, equals(kPrimaryButton));
260
      expect(events[3], isA<PointerMoveEvent>());
261
      expect(events[3].buttons, equals(kSecondaryStylusButton));
262
      expect(events[4], isA<PointerUpEvent>());
263 264 265 266
      expect(events[4].buttons, equals(0));
    }
  });

267
  test('Should synthesize kPrimaryButton for unknown devices when no button is set', () {
268 269 270 271 272 273 274
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
    const PointerDeviceKind kind = PointerDeviceKind.unknown;
    final ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.down, kind: kind, physicalX: location.dx, physicalY: location.dy),
275
        ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy),
276
        ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
277
      ],
278 279
    );

280
    final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
281 282

    expect(events.length, 5);
283
    expect(events[0], isA<PointerAddedEvent>());
284
    expect(events[0].buttons, equals(0));
285
    expect(events[1], isA<PointerHoverEvent>());
286
    expect(events[1].buttons, equals(0));
287
    expect(events[2], isA<PointerDownEvent>());
288
    expect(events[2].buttons, equals(kPrimaryButton));
289
    expect(events[3], isA<PointerMoveEvent>());
290
    expect(events[3].buttons, equals(kSecondaryButton));
291
    expect(events[4], isA<PointerUpEvent>());
292 293 294
    expect(events[4].buttons, equals(0));
  });

295
  test('Should not synthesize kPrimaryButton for mouse', () {
296
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
297
    for (final PointerDeviceKind kind in <PointerDeviceKind>[
298 299 300 301 302 303 304 305 306
      PointerDeviceKind.mouse,
    ]) {
      final ui.PointerDataPacket packet = ui.PointerDataPacket(
        data: <ui.PointerData>[
          ui.PointerData(change: ui.PointerChange.add, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.hover, kind: kind, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.down, kind: kind, buttons: kMiddleMouseButton, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.move, kind: kind, buttons: kMiddleMouseButton | kSecondaryMouseButton, physicalX: location.dx, physicalY: location.dy),
          ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
307
        ],
308 309
      );

310
      final List<PointerEvent> events = PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio).toList();
311 312

      expect(events.length, 5);
313
      expect(events[0], isA<PointerAddedEvent>());
314
      expect(events[0].buttons, equals(0));
315
      expect(events[1], isA<PointerHoverEvent>());
316
      expect(events[1].buttons, equals(0));
317
      expect(events[2], isA<PointerDownEvent>());
318
      expect(events[2].buttons, equals(kMiddleMouseButton));
319
      expect(events[3], isA<PointerMoveEvent>());
320
      expect(events[3].buttons, equals(kMiddleMouseButton | kSecondaryMouseButton));
321
      expect(events[4], isA<PointerUpEvent>());
322 323 324
      expect(events[4].buttons, equals(0));
    }
  });
325
}