gesture_binding_test.dart 13.8 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
    const ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
        ui.PointerData(change: ui.PointerChange.down),
108
        ui.PointerData(),
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
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
    final ui.PointerDataPacket packet = ui.PointerDataPacket(
      data: <ui.PointerData>[
182 183 184 185 186
        ui.PointerData(change: ui.PointerChange.add, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.hover, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.down, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.move, physicalX: location.dx, physicalY: location.dy),
        ui.PointerData(change: ui.PointerChange.up, physicalX: location.dx, physicalY: location.dy),
187
      ],
188 189
    );

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

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

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

    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', () {
233
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
234
    for (final PointerDeviceKind kind in <PointerDeviceKind>[
235 236 237 238 239 240 241 242 243 244 245
      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),
246
        ],
247 248
      );

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

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

265
  test('Should synthesize kPrimaryButton for unknown devices when no button is set', () {
266 267 268 269 270 271 272
    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),
273
        ui.PointerData(change: ui.PointerChange.move, buttons: kSecondaryButton, kind: kind, physicalX: location.dx, physicalY: location.dy),
274
        ui.PointerData(change: ui.PointerChange.up, kind: kind, physicalX: location.dx, physicalY: location.dy),
275
      ],
276 277
    );

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

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

293
  test('Should not synthesize kPrimaryButton for mouse', () {
294
    final Offset location = const Offset(10.0, 10.0) * ui.window.devicePixelRatio;
295
    for (final PointerDeviceKind kind in <PointerDeviceKind>[
296 297 298 299 300 301 302 303 304
      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),
305
        ],
306 307
      );

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

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