mirror of
https://github.com/alibaba/flutter-go.git
synced 2025-07-15 03:04:25 +08:00
362 lines
14 KiB
Dart
362 lines
14 KiB
Dart
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
// Synced 2019-05-30T14:20:56.854463.
|
|
|
|
import 'package:flutter_web_ui/ui.dart' as ui;
|
|
|
|
import 'package:flutter_web/foundation.dart';
|
|
import 'package:flutter_web/gestures.dart';
|
|
import 'package:flutter_web/scheduler.dart';
|
|
import 'package:flutter_web/services.dart';
|
|
|
|
import '../flutter_test_alternative.dart';
|
|
|
|
typedef HandleEventCallback = void Function(PointerEvent event);
|
|
|
|
class TestGestureFlutterBinding extends BindingBase
|
|
with ServicesBinding, SchedulerBinding, GestureBinding {
|
|
HandleEventCallback callback;
|
|
|
|
@override
|
|
void handleEvent(PointerEvent event, HitTestEntry entry) {
|
|
super.handleEvent(event, entry);
|
|
if (callback != null) {
|
|
callback(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
TestGestureFlutterBinding _binding = TestGestureFlutterBinding();
|
|
|
|
void ensureTestGestureBinding() {
|
|
_binding ??= TestGestureFlutterBinding();
|
|
assert(GestureBinding.instance != null);
|
|
}
|
|
|
|
void main() {
|
|
setUp(ensureTestGestureBinding);
|
|
|
|
group(MouseTracker, () {
|
|
final List<PointerEnterEvent> enter = <PointerEnterEvent>[];
|
|
final List<PointerHoverEvent> move = <PointerHoverEvent>[];
|
|
final List<PointerExitEvent> exit = <PointerExitEvent>[];
|
|
final MouseTrackerAnnotation annotation = MouseTrackerAnnotation(
|
|
onEnter: (PointerEnterEvent event) => enter.add(event),
|
|
onHover: (PointerHoverEvent event) => move.add(event),
|
|
onExit: (PointerExitEvent event) => exit.add(event),
|
|
);
|
|
// Only respond to some mouse events.
|
|
final MouseTrackerAnnotation partialAnnotation = MouseTrackerAnnotation(
|
|
onEnter: (PointerEnterEvent event) => enter.add(event),
|
|
onHover: (PointerHoverEvent event) => move.add(event),
|
|
);
|
|
bool isInHitRegionOne;
|
|
bool isInHitRegionTwo;
|
|
MouseTracker tracker;
|
|
|
|
void clear() {
|
|
enter.clear();
|
|
exit.clear();
|
|
move.clear();
|
|
}
|
|
|
|
setUp(() {
|
|
clear();
|
|
isInHitRegionOne = true;
|
|
isInHitRegionTwo = false;
|
|
tracker = MouseTracker(
|
|
GestureBinding.instance.pointerRouter,
|
|
(Offset _) sync* {
|
|
if (isInHitRegionOne)
|
|
yield annotation;
|
|
else if (isInHitRegionTwo) yield partialAnnotation;
|
|
},
|
|
);
|
|
});
|
|
|
|
test('receives and processes mouse hover events', () {
|
|
final ui.PointerDataPacket packet1 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 0.0 * ui.window.devicePixelRatio,
|
|
physicalY: 0.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
final ui.PointerDataPacket packet2 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
const ui.PointerDataPacket packet3 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.remove,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
final ui.PointerDataPacket packet4 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 201.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
final ui.PointerDataPacket packet5 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 301.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
device: 1,
|
|
),
|
|
]);
|
|
tracker.attachAnnotation(annotation);
|
|
isInHitRegionOne = true;
|
|
ui.window.onPointerDataPacket(packet1);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(0.0, 0.0)));
|
|
expect(enter.first.device, equals(0));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(0.0, 0.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
clear();
|
|
|
|
ui.window.onPointerDataPacket(packet2);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(0), reason: 'enter contains $enter');
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
clear();
|
|
|
|
ui.window.onPointerDataPacket(packet3);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(0), reason: 'enter contains $enter');
|
|
expect(move.length, equals(0), reason: 'move contains $move');
|
|
expect(exit.length, equals(1), reason: 'exit contains $exit');
|
|
expect(exit.first.position, isNull);
|
|
expect(exit.first.device, isNull);
|
|
expect(exit.first.runtimeType, equals(PointerExitEvent));
|
|
|
|
clear();
|
|
ui.window.onPointerDataPacket(packet4);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(1.0, 201.0)));
|
|
expect(enter.first.device, equals(0));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 201.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
|
|
// add in a second mouse simultaneously.
|
|
clear();
|
|
ui.window.onPointerDataPacket(packet5);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(1.0, 301.0)));
|
|
expect(enter.first.device, equals(1));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
expect(move.length, equals(2), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 201.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
expect(move.last.position, equals(const Offset(1.0, 301.0)));
|
|
expect(move.last.device, equals(1));
|
|
expect(move.last.runtimeType, equals(PointerHoverEvent));
|
|
});
|
|
test('detects exit when annotated layer no longer hit', () {
|
|
final ui.PointerDataPacket packet1 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 0.0 * ui.window.devicePixelRatio,
|
|
physicalY: 0.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
final ui.PointerDataPacket packet2 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 201.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
isInHitRegionOne = true;
|
|
tracker.attachAnnotation(annotation);
|
|
|
|
ui.window.onPointerDataPacket(packet1);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(enter.first.device, equals(0));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
// Simulate layer going away by detaching it.
|
|
clear();
|
|
isInHitRegionOne = false;
|
|
|
|
ui.window.onPointerDataPacket(packet2);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(0), reason: 'enter contains $enter');
|
|
expect(move.length, equals(0), reason: 'enter contains $move');
|
|
expect(exit.length, equals(1), reason: 'enter contains $exit');
|
|
expect(exit.first.position, const Offset(1.0, 201.0));
|
|
expect(exit.first.device, equals(0));
|
|
expect(exit.first.runtimeType, equals(PointerExitEvent));
|
|
|
|
// Actually detatch annotation. Shouldn't receive hit.
|
|
tracker.detachAnnotation(annotation);
|
|
clear();
|
|
isInHitRegionOne = false;
|
|
|
|
ui.window.onPointerDataPacket(packet2);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(0), reason: 'enter contains $enter');
|
|
expect(move.length, equals(0), reason: 'enter contains $move');
|
|
expect(exit.length, equals(0), reason: 'enter contains $exit');
|
|
});
|
|
|
|
test("don't flip out if not all mouse events are listened to", () {
|
|
final ui.PointerDataPacket packet =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
|
|
isInHitRegionOne = false;
|
|
isInHitRegionTwo = true;
|
|
tracker.attachAnnotation(partialAnnotation);
|
|
|
|
ui.window.onPointerDataPacket(packet);
|
|
tracker.collectMousePositions();
|
|
tracker.detachAnnotation(partialAnnotation);
|
|
isInHitRegionTwo = false;
|
|
});
|
|
test('detects exit when mouse goes away', () {
|
|
final ui.PointerDataPacket packet1 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 0.0 * ui.window.devicePixelRatio,
|
|
physicalY: 0.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
const ui.PointerDataPacket packet2 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.remove,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
isInHitRegionOne = true;
|
|
tracker.attachAnnotation(annotation);
|
|
ui.window.onPointerDataPacket(packet1);
|
|
tracker.collectMousePositions();
|
|
ui.window.onPointerDataPacket(packet2);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(enter.first.device, equals(0));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
expect(exit.length, equals(1), reason: 'exit contains $exit');
|
|
expect(exit.first.position, isNull);
|
|
expect(exit.first.device, isNull);
|
|
expect(exit.first.runtimeType, equals(PointerExitEvent));
|
|
});
|
|
test('handles mouse down and move', () {
|
|
final ui.PointerDataPacket packet1 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 0.0 * ui.window.devicePixelRatio,
|
|
physicalY: 0.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
ui.PointerData(
|
|
change: ui.PointerChange.hover,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
final ui.PointerDataPacket packet2 =
|
|
ui.PointerDataPacket(data: <ui.PointerData>[
|
|
ui.PointerData(
|
|
change: ui.PointerChange.down,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 101.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
ui.PointerData(
|
|
change: ui.PointerChange.move,
|
|
physicalX: 1.0 * ui.window.devicePixelRatio,
|
|
physicalY: 201.0 * ui.window.devicePixelRatio,
|
|
kind: PointerDeviceKind.mouse,
|
|
),
|
|
]);
|
|
isInHitRegionOne = true;
|
|
tracker.attachAnnotation(annotation);
|
|
ui.window.onPointerDataPacket(packet1);
|
|
tracker.collectMousePositions();
|
|
ui.window.onPointerDataPacket(packet2);
|
|
tracker.collectMousePositions();
|
|
expect(enter.length, equals(1), reason: 'enter contains $enter');
|
|
expect(enter.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(enter.first.device, equals(0));
|
|
expect(enter.first.runtimeType, equals(PointerEnterEvent));
|
|
expect(move.length, equals(1), reason: 'move contains $move');
|
|
expect(move.first.position, equals(const Offset(1.0, 101.0)));
|
|
expect(move.first.device, equals(0));
|
|
expect(move.first.runtimeType, equals(PointerHoverEvent));
|
|
expect(exit.length, equals(0), reason: 'exit contains $exit');
|
|
});
|
|
});
|
|
}
|