Unverified Commit da8695d3 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Mouse events report correct local positions (#61190)

parent 067d3da8
......@@ -5,7 +5,6 @@
// @dart = 2.8
import 'dart:async';
import 'dart:collection' show LinkedHashSet;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
......@@ -52,7 +51,7 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
// The `annotations` is the current annotations that the device is hovering in
// visual order from front the back.
// The return value is never null.
MouseCursor _findFirstCursor(LinkedHashSet<MouseTrackerAnnotation> annotations) {
MouseCursor _findFirstCursor(Iterable<MouseTrackerAnnotation> annotations) {
return _DeferringMouseCursor.firstNonDeferred(
annotations.map((MouseTrackerAnnotation annotation) => annotation.cursor),
) ?? SystemMouseCursors.basic;
......@@ -68,7 +67,7 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
}
final MouseCursorSession lastSession = _lastSession[device];
final MouseCursor nextCursor = _findFirstCursor(details.nextAnnotations);
final MouseCursor nextCursor = _findFirstCursor(details.nextAnnotations.keys);
if (lastSession?.cursor == nextCursor)
return;
......
......@@ -4,13 +4,15 @@
// @dart = 2.8
import 'dart:collection' show LinkedHashSet;
import 'dart:collection' show LinkedHashMap;
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/scheduler.dart';
import 'package:vector_math/vector_math_64.dart' show Matrix4;
import 'mouse_cursor.dart';
import 'object.dart';
......@@ -132,7 +134,7 @@ class MouseTrackerAnnotation with Diagnosticable {
///
/// It is used by the [BaseMouseTracker] to fetch annotations for the mouse
/// position.
typedef MouseDetectorAnnotationFinder = Iterable<MouseTrackerAnnotation> Function(Offset offset);
typedef MouseDetectorAnnotationFinder = LinkedHashMap<MouseTrackerAnnotation, Matrix4> Function(Offset offset);
// Various states of a connected mouse device used by [BaseMouseTracker].
class _MouseState {
......@@ -143,13 +145,13 @@ class _MouseState {
// The list of annotations that contains this device.
//
// It uses [LinkedHashSet] to keep the insertion order.
LinkedHashSet<MouseTrackerAnnotation> get annotations => _annotations;
LinkedHashSet<MouseTrackerAnnotation> _annotations = LinkedHashSet<MouseTrackerAnnotation>();
// It uses [LinkedHashMap] to keep the insertion order.
LinkedHashMap<MouseTrackerAnnotation, Matrix4> get annotations => _annotations;
LinkedHashMap<MouseTrackerAnnotation, Matrix4> _annotations = LinkedHashMap<MouseTrackerAnnotation, Matrix4>();
LinkedHashSet<MouseTrackerAnnotation> replaceAnnotations(LinkedHashSet<MouseTrackerAnnotation> value) {
LinkedHashMap<MouseTrackerAnnotation, Matrix4> replaceAnnotations(LinkedHashMap<MouseTrackerAnnotation, Matrix4> value) {
assert(value != null);
final LinkedHashSet<MouseTrackerAnnotation> previous = _annotations;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> previous = _annotations;
_annotations = value;
return previous;
}
......@@ -215,12 +217,12 @@ class MouseTrackerUpdateDetails with Diagnosticable {
/// The annotations that the device is hovering before the update.
///
/// It is never null.
final LinkedHashSet<MouseTrackerAnnotation> lastAnnotations;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations;
/// The annotations that the device is hovering after the update.
///
/// It is never null.
final LinkedHashSet<MouseTrackerAnnotation> nextAnnotations;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations;
/// The last event that the device observed before the update.
///
......@@ -259,8 +261,8 @@ class MouseTrackerUpdateDetails with Diagnosticable {
properties.add(IntProperty('device', device));
properties.add(DiagnosticsProperty<PointerEvent>('previousEvent', previousEvent));
properties.add(DiagnosticsProperty<PointerEvent>('triggeringEvent', triggeringEvent));
properties.add(DiagnosticsProperty<Set<MouseTrackerAnnotation>>('lastAnnotations', lastAnnotations));
properties.add(DiagnosticsProperty<Set<MouseTrackerAnnotation>>('nextAnnotations', nextAnnotations));
properties.add(DiagnosticsProperty<Map<MouseTrackerAnnotation, Matrix4>>('lastAnnotations', lastAnnotations));
properties.add(DiagnosticsProperty<Map<MouseTrackerAnnotation, Matrix4>>('nextAnnotations', nextAnnotations));
}
}
......@@ -418,16 +420,17 @@ class BaseMouseTracker extends ChangeNotifier {
|| lastEvent.position != event.position;
}
// Find the annotations that is hovered by the device of the `state`.
// Find the annotations that is hovered by the device of the `state`, and
// their respective global transform matrices.
//
// If the device is not connected, an empty set is returned without calling
// `annotationFinder`.
LinkedHashSet<MouseTrackerAnnotation> _findAnnotations(_MouseState state) {
// If the device is not connected or not a mouse, an empty map is returned
// without calling `annotationFinder`.
LinkedHashMap<MouseTrackerAnnotation, Matrix4> _findAnnotations(_MouseState state) {
final Offset globalPosition = state.latestEvent.position;
final int device = state.device;
return (_mouseStates.containsKey(device))
? LinkedHashSet<MouseTrackerAnnotation>.from(annotationFinder(globalPosition))
: <MouseTrackerAnnotation>{} as LinkedHashSet<MouseTrackerAnnotation>;
if (!_mouseStates.containsKey(device))
return <MouseTrackerAnnotation, Matrix4>{} as LinkedHashMap<MouseTrackerAnnotation, Matrix4>;
return annotationFinder(globalPosition);
}
/// A callback that is called on the update of a device.
......@@ -485,8 +488,8 @@ class BaseMouseTracker extends ChangeNotifier {
final _MouseState targetState = _mouseStates[device] ?? existingState;
final PointerEvent lastEvent = targetState.replaceLatestEvent(event);
final LinkedHashSet<MouseTrackerAnnotation> nextAnnotations = _findAnnotations(targetState);
final LinkedHashSet<MouseTrackerAnnotation> lastAnnotations = targetState.replaceAnnotations(nextAnnotations);
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations = _findAnnotations(targetState);
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = targetState.replaceAnnotations(nextAnnotations);
handleDeviceUpdate(MouseTrackerUpdateDetails.byPointerEvent(
lastAnnotations: lastAnnotations,
......@@ -506,8 +509,8 @@ class BaseMouseTracker extends ChangeNotifier {
_deviceUpdatePhase(() {
for (final _MouseState dirtyState in _mouseStates.values) {
final PointerEvent lastEvent = dirtyState.latestEvent;
final LinkedHashSet<MouseTrackerAnnotation> nextAnnotations = _findAnnotations(dirtyState);
final LinkedHashSet<MouseTrackerAnnotation> lastAnnotations = dirtyState.replaceAnnotations(nextAnnotations);
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations = _findAnnotations(dirtyState);
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = dirtyState.replaceAnnotations(nextAnnotations);
handleDeviceUpdate(MouseTrackerUpdateDetails.byNewFrame(
lastAnnotations: lastAnnotations,
......@@ -531,8 +534,8 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
final PointerEvent triggeringEvent = details.triggeringEvent;
final PointerEvent latestEvent = details.latestEvent;
final LinkedHashSet<MouseTrackerAnnotation> lastAnnotations = details.lastAnnotations;
final LinkedHashSet<MouseTrackerAnnotation> nextAnnotations = details.nextAnnotations;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = details.lastAnnotations;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> nextAnnotations = details.nextAnnotations;
// Order is important for mouse event callbacks. The `findAnnotations`
// returns annotations in the visual order from front to back. We call
......@@ -542,19 +545,22 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
// Send exit events to annotations that are in last but not in next, in
// visual order.
final Iterable<MouseTrackerAnnotation> exitingAnnotations = lastAnnotations.difference(nextAnnotations);
for (final MouseTrackerAnnotation annotation in exitingAnnotations) {
if (annotation.onExit != null)
annotation.onExit(PointerExitEvent.fromMouseEvent(latestEvent));
}
final PointerExitEvent baseExitEvent = PointerExitEvent.fromMouseEvent(latestEvent);
lastAnnotations.forEach((MouseTrackerAnnotation annotation, Matrix4 transform) {
if (!nextAnnotations.containsKey(annotation))
if (annotation.onExit != null)
annotation.onExit(baseExitEvent.transformed(lastAnnotations[annotation]));
});
// Send enter events to annotations that are not in last but in next, in
// reverse visual order.
final Iterable<MouseTrackerAnnotation> enteringAnnotations =
nextAnnotations.difference(lastAnnotations).toList().reversed;
for (final MouseTrackerAnnotation annotation in enteringAnnotations) {
final List<MouseTrackerAnnotation> enteringAnnotations = nextAnnotations.keys.where(
(MouseTrackerAnnotation annotation) => !lastAnnotations.containsKey(annotation),
).toList();
final PointerEnterEvent baseEnterEvent = PointerEnterEvent.fromMouseEvent(latestEvent);
for (final MouseTrackerAnnotation annotation in enteringAnnotations.reversed) {
if (annotation.onEnter != null)
annotation.onEnter(PointerEnterEvent.fromMouseEvent(latestEvent));
annotation.onEnter(baseEnterEvent.transformed(nextAnnotations[annotation]));
}
// Send hover events to annotations that are in next, in reverse visual
......@@ -567,10 +573,10 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
// last hover, then trigger the hover callback on all annotations.
// Otherwise, trigger the hover callback only on annotations that it
// newly enters.
final Iterable<MouseTrackerAnnotation> hoveringAnnotations = pointerHasMoved ? nextAnnotations.toList().reversed : enteringAnnotations;
final Iterable<MouseTrackerAnnotation> hoveringAnnotations = pointerHasMoved ? nextAnnotations.keys.toList().reversed : enteringAnnotations;
for (final MouseTrackerAnnotation annotation in hoveringAnnotations) {
if (annotation.onHover != null) {
annotation.onHover(triggeringEvent);
annotation.onHover(triggeringEvent.transformed(nextAnnotations[annotation]));
}
}
}
......
......@@ -4,6 +4,7 @@
// @dart = 2.8
import 'dart:collection' show LinkedHashMap;
import 'dart:developer';
import 'dart:io' show Platform;
import 'dart:ui' as ui show Scene, SceneBuilder, Window;
......@@ -198,7 +199,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
///
/// * [Layer.findAllAnnotations], which is used by this method to find all
/// [AnnotatedRegionLayer]s annotated for mouse tracking.
Iterable<MouseTrackerAnnotation> hitTestMouseTrackers(Offset position) {
LinkedHashMap<MouseTrackerAnnotation, Matrix4> hitTestMouseTrackers(Offset position) {
// Layer hit testing is done using device pixels, so we have to convert
// the logical coordinates of the event location back to device pixels
// here.
......@@ -206,10 +207,11 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
if (child != null)
child.hitTest(result, position: position);
result.add(HitTestEntry(this));
final List<MouseTrackerAnnotation> annotations = <MouseTrackerAnnotation>[];
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> annotations = <MouseTrackerAnnotation, Matrix4>{}
as LinkedHashMap<MouseTrackerAnnotation, Matrix4>;
for (final HitTestEntry entry in result.path) {
if (entry.target is MouseTrackerAnnotation) {
annotations.add(entry.target as MouseTrackerAnnotation);
annotations[entry.target as MouseTrackerAnnotation] = entry.transform;
}
}
return annotations;
......
......@@ -4,6 +4,7 @@
// @dart = 2.8
import 'dart:collection' show LinkedHashMap;
import 'dart:ui' as ui;
import 'dart:ui' show PointerChange;
......@@ -25,12 +26,14 @@ void _ensureTestGestureBinding() {
assert(GestureBinding.instance != null);
}
typedef SimpleAnnotationFinder = Iterable<MouseTrackerAnnotation> Function(Offset offset);
void main() {
MethodCallHandler _methodCallHandler;
// Only one of `logCursors` and `cursorHandler` should be specified.
void _setUpMouseTracker({
MouseDetectorAnnotationFinder annotationFinder,
SimpleAnnotationFinder annotationFinder,
List<_CursorUpdateDetails> logCursors,
MethodCallHandler cursorHandler,
}) {
......@@ -43,7 +46,11 @@ void main() {
: cursorHandler;
final MouseTracker mouseTracker = MouseTracker(
GestureBinding.instance.pointerRouter,
annotationFinder,
(Offset offset) => LinkedHashMap<MouseTrackerAnnotation, Matrix4>.fromEntries(
annotationFinder(offset).map(
(MouseTrackerAnnotation annotation) => MapEntry<MouseTrackerAnnotation, Matrix4>(annotation, Matrix4.identity()),
),
),
);
RendererBinding.instance.initMouseTracker(mouseTracker);
}
......
......@@ -4,6 +4,7 @@
// @dart = 2.8
import 'dart:collection' show LinkedHashMap;
import 'dart:ui' as ui;
import 'dart:ui' show PointerChange;
......@@ -13,6 +14,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart' show Matrix4;
import '../flutter_test_alternative.dart';
......@@ -65,11 +67,29 @@ void _ensureTestGestureBinding() {
assert(GestureBinding.instance != null);
}
@immutable
class AnnotationEntry {
AnnotationEntry(this.annotation, [Matrix4 transform])
: transform = transform ?? Matrix4.identity();
final MouseTrackerAnnotation annotation;
final Matrix4 transform;
}
typedef SimpleAnnotationFinder = Iterable<AnnotationEntry> Function(Offset offset);
void main() {
void _setUpMouseAnnotationFinder(MouseDetectorAnnotationFinder annotationFinder) {
void _setUpMouseAnnotationFinder(SimpleAnnotationFinder annotationFinder) {
final MouseTracker mouseTracker = MouseTracker(
GestureBinding.instance.pointerRouter,
annotationFinder,
(Offset offset) => LinkedHashMap<MouseTrackerAnnotation, Matrix4>.fromEntries(
annotationFinder(offset).map(
(AnnotationEntry entry) => MapEntry<MouseTrackerAnnotation, Matrix4>(
entry.annotation,
entry.transform,
),
),
),
);
RendererBinding.instance.initMouseTracker(mouseTracker);
}
......@@ -96,7 +116,7 @@ void main() {
);
_setUpMouseAnnotationFinder(
(Offset position) sync* {
yield annotation;
yield AnnotationEntry(annotation);
},
);
return annotation;
......@@ -312,7 +332,7 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInHitRegion) {
yield annotation;
yield AnnotationEntry(annotation, Matrix4.translationValues(10, 20, 0));
}
});
......@@ -334,7 +354,7 @@ void main() {
_binding.flushPostFrameCallbacks(Duration.zero);
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerEnterEvent(position: Offset(0.0, 100.0)),
const PointerEnterEvent(position: Offset(0, 100), localPosition: Offset(10, 120)),
]));
events.clear();
......@@ -345,7 +365,7 @@ void main() {
_binding.flushPostFrameCallbacks(Duration.zero);
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerExitEvent(position: Offset(0.0, 100.0)),
const PointerExitEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
expect(_binding.postFrameCallbacks, hasLength(0));
});
......@@ -360,7 +380,7 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInHitRegion) {
yield annotation;
yield AnnotationEntry(annotation, Matrix4.translationValues(10, 20, 0));
}
});
......@@ -380,7 +400,7 @@ void main() {
_binding.flushPostFrameCallbacks(Duration.zero);
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerEnterEvent(position: Offset(0.0, 100.0)),
const PointerEnterEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
events.clear();
......@@ -394,7 +414,7 @@ void main() {
_binding.flushPostFrameCallbacks(Duration.zero);
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerExitEvent(position: Offset(0.0, 100.0)),
const PointerExitEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
expect(_binding.postFrameCallbacks, hasLength(0));
});
......@@ -409,7 +429,7 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInHitRegion) {
yield annotation;
yield AnnotationEntry(annotation, Matrix4.translationValues(10, 20, 0));
}
});
......@@ -423,7 +443,7 @@ void main() {
expect(_binding.postFrameCallbacks, hasLength(0));
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerEnterEvent(position: Offset(0.0, 100.0)),
const PointerEnterEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
events.clear();
......@@ -433,7 +453,7 @@ void main() {
]));
expect(_binding.postFrameCallbacks, hasLength(0));
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerExitEvent(position: Offset(0.0, 100.0)),
const PointerExitEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
});
......@@ -447,7 +467,7 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInHitRegion) {
yield annotation;
yield AnnotationEntry(annotation, Matrix4.translationValues(10, 20, 0));
}
});
......@@ -466,8 +486,8 @@ void main() {
]));
expect(_binding.postFrameCallbacks, hasLength(0));
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerEnterEvent(position: Offset(0.0, 100.0)),
const PointerHoverEvent(position: Offset(0.0, 100.0)),
const PointerEnterEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
const PointerHoverEvent(position: Offset(0.0, 100.0), localPosition: Offset(10, 120)),
]));
events.clear();
......@@ -478,7 +498,7 @@ void main() {
]));
expect(_binding.postFrameCallbacks, hasLength(0));
expect(events, _equalToEventsOnCriticalFields(<PointerEvent>[
const PointerExitEvent(position: Offset(200.0, 100.0)),
const PointerExitEvent(position: Offset(200.0, 100.0), localPosition: Offset(210, 120)),
]));
});
......@@ -506,9 +526,9 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInHitRegionOne)
yield annotation1;
yield AnnotationEntry(annotation1);
else if (isInHitRegionTwo)
yield annotation2;
yield AnnotationEntry(annotation2);
});
isInHitRegionOne = false;
......@@ -546,8 +566,8 @@ void main() {
_setUpMouseAnnotationFinder((Offset position) sync* {
// Children's annotations come before parents'.
if (isInB) {
yield annotationB;
yield annotationA;
yield AnnotationEntry(annotationB);
yield AnnotationEntry(annotationA);
}
});
......@@ -598,9 +618,9 @@ void main() {
);
_setUpMouseAnnotationFinder((Offset position) sync* {
if (isInA) {
yield annotationA;
yield AnnotationEntry(annotationA);
} else if (isInB) {
yield annotationB;
yield AnnotationEntry(annotationB);
}
});
......@@ -676,7 +696,8 @@ class _EventCriticalFieldsMatcher extends Matcher {
if (!(
_matchesField(matchState, 'kind', actual.kind, PointerDeviceKind.mouse) &&
_matchesField(matchState, 'position', actual.position, _expected.position) &&
_matchesField(matchState, 'device', actual.device, _expected.device)
_matchesField(matchState, 'device', actual.device, _expected.device) &&
_matchesField(matchState, 'localPosition', actual.localPosition, _expected.localPosition)
)) {
return false;
}
......
......@@ -4,6 +4,7 @@
// @dart = 2.8
import 'dart:collection' show LinkedHashMap;
import 'dart:typed_data';
import 'dart:ui' as ui show Gradient, Image, ImageFilter;
......@@ -492,7 +493,7 @@ void main() {
test('RenderMouseRegion can change properties when detached', () {
renderer.initMouseTracker(MouseTracker(
renderer.pointerRouter,
(_) => <MouseTrackerAnnotation>[],
(_) => <MouseTrackerAnnotation, Matrix4>{} as LinkedHashMap<MouseTrackerAnnotation, Matrix4>,
));
final RenderMouseRegion object = RenderMouseRegion();
object
......
......@@ -112,8 +112,10 @@ void main() {
await gesture.moveTo(const Offset(400.0, 300.0));
expect(move, isNotNull);
expect(move.position, equals(const Offset(400.0, 300.0)));
expect(move.localPosition, equals(const Offset(50.0, 50.0)));
expect(enter, isNotNull);
expect(enter.position, equals(const Offset(400.0, 300.0)));
expect(enter.localPosition, equals(const Offset(50.0, 50.0)));
expect(exit, isNull);
});
......@@ -145,6 +147,7 @@ void main() {
expect(enter, isNull);
expect(exit, isNotNull);
expect(exit.position, equals(const Offset(1.0, 1.0)));
expect(exit.localPosition, equals(const Offset(-349.0, -249.0)));
});
testWidgets('triggers pointer enter when a mouse is connected', (WidgetTester tester) async {
......@@ -170,6 +173,7 @@ void main() {
expect(move, isNull);
expect(enter, isNotNull);
expect(enter.position, equals(const Offset(400.0, 300.0)));
expect(enter.localPosition, equals(const Offset(50.0, 50.0)));
expect(exit, isNull);
});
......@@ -203,6 +207,7 @@ void main() {
expect(enter, isNull);
expect(exit, isNotNull);
expect(exit.position, equals(const Offset(400.0, 300.0)));
expect(exit.localPosition, equals(const Offset(50.0, 50.0)));
exit = null;
await tester.pump();
expect(move, isNull);
......@@ -243,6 +248,7 @@ void main() {
expect(move, isNull);
expect(enter, isNotNull);
expect(enter.position, equals(const Offset(400.0, 300.0)));
expect(enter.localPosition, equals(const Offset(50.0, 50.0)));
expect(exit, isNull);
});
......@@ -285,7 +291,7 @@ void main() {
PointerHoverEvent move;
PointerExitEvent exit;
await tester.pumpWidget(Container(
alignment: Alignment.center,
alignment: Alignment.topLeft,
child: MouseRegion(
child: const SizedBox(
width: 100.0,
......@@ -297,14 +303,14 @@ void main() {
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: const Offset(1.0, 1.0));
await gesture.addPointer(location: const Offset(401.0, 301.0));
addTearDown(gesture.removePointer);
await tester.pump();
expect(enter, isNull);
expect(move, isNull);
expect(exit, isNull);
await tester.pumpWidget(Container(
alignment: Alignment.topLeft,
alignment: Alignment.center,
child: MouseRegion(
child: const SizedBox(
width: 100.0,
......@@ -317,7 +323,8 @@ void main() {
));
await tester.pump();
expect(enter, isNotNull);
expect(enter.position, equals(const Offset(1.0, 1.0)));
expect(enter.position, equals(const Offset(401.0, 301.0)));
expect(enter.localPosition, equals(const Offset(51.0, 51.0)));
expect(move, isNull);
expect(exit, isNull);
});
......@@ -362,6 +369,7 @@ void main() {
expect(move, isNull);
expect(exit, isNotNull);
expect(exit.position, equals(const Offset(400, 300)));
expect(exit.localPosition, equals(const Offset(50, 50)));
});
testWidgets('Hover works with nested listeners', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment