Unverified Commit 2cdf2f00 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Treat hover events as normal pointer events, and bring them back to Listener (#63834)

parent 2cdec258
......@@ -270,7 +270,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
void handlePointerEvent(PointerEvent event) {
assert(!locked);
HitTestResult? hitTestResult;
if (event is PointerDownEvent || event is PointerSignalEvent) {
if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent) {
assert(!_hitTests.containsKey(event.pointer));
hitTestResult = HitTestResult();
hitTest(hitTestResult, event.position);
......@@ -298,7 +298,6 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
return true;
}());
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is PointerAddedEvent ||
event is PointerRemovedEvent) {
assert(event.position != null);
......@@ -318,8 +317,8 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
/// null, the event is also sent to every [HitTestTarget] in the entries of the
/// given [HitTestResult]. Any exceptions from the handlers are caught.
///
/// The `hitTestResult` argument may only be null for [PointerHoverEvent]s,
/// [PointerAddedEvent]s, or [PointerRemovedEvent]s.
/// The `hitTestResult` argument may only be null for [PointerAddedEvent]s or
/// [PointerRemovedEvent]s.
@override // from HitTestDispatcher
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
assert(!locked);
......@@ -327,7 +326,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
// [PointerAddedEvent], or [PointerRemovedEvent]. These events are specially
// routed here; other events will be routed through the `handleEvent` below.
if (hitTestResult == null) {
assert(event is PointerHoverEvent || event is PointerAddedEvent || event is PointerRemovedEvent);
assert(event is PointerAddedEvent || event is PointerRemovedEvent);
try {
pointerRouter.route(event);
} catch (exception, stack) {
......
......@@ -259,7 +259,6 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
@override // from GestureBinding
void dispatchEvent(PointerEvent event, HitTestResult? hitTestResult) {
if (hitTestResult != null ||
event is PointerHoverEvent ||
event is PointerAddedEvent ||
event is PointerRemovedEvent) {
assert(event.position != null);
......
......@@ -53,7 +53,6 @@ class MouseTrackerAnnotation with Diagnosticable {
/// All arguments are optional. The [cursor] must not be null.
const MouseTrackerAnnotation({
this.onEnter,
this.onHover,
this.onExit,
this.cursor = MouseCursor.defer,
}) : assert(cursor != null);
......@@ -72,16 +71,6 @@ class MouseTrackerAnnotation with Diagnosticable {
/// * [MouseRegion.onEnter], which uses this callback.
final PointerEnterEventListener? onEnter;
/// Triggered when a mouse pointer has moved onto or within the region without
/// buttons pressed.
///
/// This callback is not triggered by the movement of an annotation.
///
/// See also:
///
/// * [MouseRegion.onHover], which uses this callback.
final PointerHoverEventListener? onHover;
/// Triggered when a mouse pointer, with or without buttons pressed, has
/// exited the region.
///
......@@ -482,8 +471,6 @@ abstract class BaseMouseTracker extends ChangeNotifier {
mixin _MouseTrackerEventMixin on BaseMouseTracker {
// Handles device update and dispatches mouse event callbacks.
static void _handleDeviceUpdateMouseEvents(MouseTrackerUpdateDetails details) {
final PointerEvent? previousEvent = details.previousEvent;
final PointerEvent? triggeringEvent = details.triggeringEvent;
final PointerEvent latestEvent = details.latestEvent;
final LinkedHashMap<MouseTrackerAnnotation, Matrix4> lastAnnotations = details.lastAnnotations;
......@@ -514,24 +501,6 @@ mixin _MouseTrackerEventMixin on BaseMouseTracker {
if (annotation.onEnter != null)
annotation.onEnter!(baseEnterEvent.transformed(nextAnnotations[annotation]));
}
// Send hover events to annotations that are in next, in reverse visual
// order. The reverse visual order is chosen only because of the simplicity
// by keeping the hover events aligned with enter events.
if (triggeringEvent is PointerHoverEvent) {
final Offset? hoverPositionBeforeUpdate = previousEvent is PointerHoverEvent ? previousEvent.position : null;
final bool pointerHasMoved = hoverPositionBeforeUpdate == null || hoverPositionBeforeUpdate != triggeringEvent.position;
// If the hover event follows a non-hover event, or has moved since the
// 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.keys.toList().reversed : enteringAnnotations;
for (final MouseTrackerAnnotation annotation in hoveringAnnotations) {
if (annotation.onHover != null) {
annotation.onHover!(triggeringEvent.transformed(nextAnnotations[annotation]));
}
}
}
}
@protected
......
......@@ -723,13 +723,6 @@ mixin _PlatformViewGestureMixin on RenderBox implements MouseTrackerAnnotation {
@override
PointerEnterEventListener? get onEnter => null;
@override
PointerHoverEventListener get onHover => _handleHover;
void _handleHover(PointerHoverEvent event) {
if (_handlePointerEvent != null)
_handlePointerEvent!(event);
}
@override
PointerExitEventListener? get onExit => null;
......@@ -741,6 +734,9 @@ mixin _PlatformViewGestureMixin on RenderBox implements MouseTrackerAnnotation {
if (event is PointerDownEvent) {
_gestureRecognizer!.addPointer(event);
}
if (event is PointerHoverEvent) {
_handlePointerEvent?.call(event);
}
}
@override
......
......@@ -2662,10 +2662,10 @@ typedef PointerSignalEventListener = void Function(PointerSignalEvent event);
/// Calls callbacks in response to common pointer events.
///
/// It responds to events that can construct gestures, such as when the
/// pointer is pressed, moved, then released or canceled.
/// pointer is pointer is pressed and moved, and then released or canceled.
///
/// It does not respond to events that are exclusive to mouse, such as when the
/// mouse enters, exits or hovers a region without pressing any buttons. For
/// mouse enters and exits a region without pressing any buttons. For
/// these events, use [RenderMouseRegion].
///
/// If it has a child, defers to the child for sizing behavior.
......@@ -2679,6 +2679,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onPointerHover,
this.onPointerCancel,
this.onPointerSignal,
HitTestBehavior behavior = HitTestBehavior.deferToChild,
......@@ -2697,6 +2698,9 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
/// contact with the screen.
PointerUpEventListener? onPointerUp;
/// Called when a pointer that has not an [onPointerDown] changes position.
PointerHoverEventListener? onPointerHover;
/// Called when the input from a pointer that triggered an [onPointerDown] is
/// no longer directed towards this receiver.
PointerCancelEventListener? onPointerCancel;
......@@ -2712,16 +2716,18 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
@override
void handleEvent(PointerEvent event, HitTestEntry entry) {
assert(debugHandleEvent(event, entry));
if (onPointerDown != null && event is PointerDownEvent)
return onPointerDown!(event);
if (onPointerMove != null && event is PointerMoveEvent)
return onPointerMove!(event);
if (onPointerUp != null && event is PointerUpEvent)
return onPointerUp!(event);
if (onPointerCancel != null && event is PointerCancelEvent)
return onPointerCancel!(event);
if (onPointerSignal != null && event is PointerSignalEvent)
return onPointerSignal!(event);
if (event is PointerDownEvent)
return onPointerDown?.call(event);
if (event is PointerMoveEvent)
return onPointerMove?.call(event);
if (event is PointerUpEvent)
return onPointerUp?.call(event);
if (event is PointerHoverEvent)
return onPointerHover?.call(event);
if (event is PointerCancelEvent)
return onPointerCancel?.call(event);
if (event is PointerSignalEvent)
return onPointerSignal?.call(event);
}
@override
......@@ -2733,6 +2739,7 @@ class RenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
'down': onPointerDown,
'move': onPointerMove,
'up': onPointerUp,
'hover': onPointerHover,
'cancel': onPointerCancel,
'signal': onPointerSignal,
},
......@@ -2821,7 +2828,10 @@ class RenderMouseRegion extends RenderProxyBox implements MouseTrackerAnnotation
@override
PointerEnterEventListener? onEnter;
@override
/// Triggered when a pointer has moved onto or within the region without
/// buttons pressed.
///
/// This callback is not triggered by the movement of the object.
PointerHoverEventListener? onHover;
@override
......
......@@ -848,6 +848,10 @@ abstract class AndroidViewController extends PlatformViewController {
/// for description of the parameters.
@override
Future<void> dispatchPointerEvent(PointerEvent event) async {
if (event is PointerHoverEvent) {
return;
}
if (event is PointerDownEvent) {
_motionEventConverter.handlePointerDownEvent(event);
}
......
......@@ -5812,27 +5812,8 @@ class Listener extends StatelessWidget {
Key? key,
this.onPointerDown,
this.onPointerMove,
// We have to ignore the lint rule here in order to use deprecated
// parameters and keep backward compatibility.
// TODO(tongmu): After it goes stable, remove these 3 parameters from Listener
// and Listener should no longer need an intermediate class _PointerListener.
// https://github.com/flutter/flutter/issues/36085
@Deprecated(
'Use MouseRegion.onEnter instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerEnter,
@Deprecated(
'Use MouseRegion.onExit instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerExit,
@Deprecated(
'Use MouseRegion.onHover instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerHover,
this.onPointerUp,
this.onPointerHover,
this.onPointerCancel,
this.onPointerSignal,
this.behavior = HitTestBehavior.deferToChild,
......@@ -5849,15 +5830,9 @@ class Listener extends StatelessWidget {
/// Called when a pointer that triggered an [onPointerDown] changes position.
final PointerMoveEventListener? onPointerMove;
/// Called when a pointer enters the region for this widget.
///
/// This is only fired for pointers which report their location when not down
/// (e.g. mouse pointers, but not most touch pointers).
///
/// If this is a mouse pointer, this will fire when the mouse pointer enters
/// the region defined by this widget, or when the widget appears under the
/// pointer.
final PointerEnterEventListener? onPointerEnter;
/// Called when a pointer that triggered an [onPointerDown] is no longer in
/// contact with the screen.
final PointerUpEventListener? onPointerUp;
/// Called when a pointer that has not triggered an [onPointerDown] changes
/// position.
......@@ -5866,20 +5841,6 @@ class Listener extends StatelessWidget {
/// (e.g. mouse pointers, but not most touch pointers).
final PointerHoverEventListener? onPointerHover;
/// Called when a pointer leaves the region for this widget.
///
/// This is only fired for pointers which report their location when not down
/// (e.g. mouse pointers, but not most touch pointers).
///
/// If this is a mouse pointer, this will fire when the mouse pointer leaves
/// the region defined by this widget, or when the widget disappears from
/// under the pointer.
final PointerExitEventListener? onPointerExit;
/// Called when a pointer that triggered an [onPointerDown] is no longer in
/// contact with the screen.
final PointerUpEventListener? onPointerUp;
/// Called when the input from a pointer that triggered an [onPointerDown] is
/// no longer directed towards this receiver.
final PointerCancelEventListener? onPointerCancel;
......@@ -5904,28 +5865,18 @@ class Listener extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget? result = _child;
if (onPointerEnter != null ||
onPointerExit != null ||
onPointerHover != null) {
result = MouseRegion(
onEnter: onPointerEnter,
onExit: onPointerExit,
onHover: onPointerHover,
opaque: false,
child: result,
);
}
result = _PointerListener(
// TODO(dkwingsmt): Remove the extra wrapper, and make `Listener` a
// StatelessWidget. https://github.com/flutter/flutter/issues/65586
return _PointerListener(
onPointerDown: onPointerDown,
onPointerUp: onPointerUp,
onPointerMove: onPointerMove,
onPointerHover: onPointerHover,
onPointerCancel: onPointerCancel,
onPointerSignal: onPointerSignal,
behavior: behavior,
child: result,
child: _child,
);
return result;
}
}
......@@ -5935,6 +5886,7 @@ class _PointerListener extends SingleChildRenderObjectWidget {
this.onPointerDown,
this.onPointerMove,
this.onPointerUp,
this.onPointerHover,
this.onPointerCancel,
this.onPointerSignal,
this.behavior = HitTestBehavior.deferToChild,
......@@ -5945,6 +5897,7 @@ class _PointerListener extends SingleChildRenderObjectWidget {
final PointerDownEventListener? onPointerDown;
final PointerMoveEventListener? onPointerMove;
final PointerUpEventListener? onPointerUp;
final PointerHoverEventListener? onPointerHover;
final PointerCancelEventListener? onPointerCancel;
final PointerSignalEventListener? onPointerSignal;
final HitTestBehavior behavior;
......@@ -5955,6 +5908,7 @@ class _PointerListener extends SingleChildRenderObjectWidget {
onPointerDown: onPointerDown,
onPointerMove: onPointerMove,
onPointerUp: onPointerUp,
onPointerHover: onPointerHover,
onPointerCancel: onPointerCancel,
onPointerSignal: onPointerSignal,
behavior: behavior,
......@@ -5967,6 +5921,7 @@ class _PointerListener extends SingleChildRenderObjectWidget {
..onPointerDown = onPointerDown
..onPointerMove = onPointerMove
..onPointerUp = onPointerUp
..onPointerHover = onPointerHover
..onPointerCancel = onPointerCancel
..onPointerSignal = onPointerSignal
..behavior = behavior;
......@@ -5987,11 +5942,15 @@ class _PointerListener extends SingleChildRenderObjectWidget {
}
}
/// A widget that tracks the movement of mice, even when no button is pressed.
/// A widget that tracks the movement of mice.
///
/// It does not listen to events that can construct gestures, such as when the
/// pointer is pressed, moved, then released or canceled. For these events,
/// use [Listener], or more preferably, [GestureDetector].
/// [MouseRegion] is used
/// when it is needed to compare the list of objects that a mouse pointer is
/// hovering over betweeen this frame and the last frame. This means entering
/// events, exiting events, and mouse cursors.
///
/// To listen to general pointer events, use [Listener], or more preferably,
/// [GestureDetector].
///
/// ## Layout behavior
///
......@@ -6110,13 +6069,23 @@ class MouseRegion extends StatefulWidget {
/// internally implemented.
final PointerEnterEventListener? onEnter;
/// Triggered when a mouse pointer has moved onto or within the widget without
/// Triggered when a pointer moves into a position within this widget without
/// buttons pressed.
///
/// This callback is not triggered by the movement of an annotation.
/// Usually this is only fired for pointers which report their location when
/// not down (e.g. mouse pointers). Certain devices also fire this event on
/// single taps in accessibility mode.
///
/// This callback is not triggered by the movement of the widget.
///
/// The time that this callback is triggered is during the callback of a
/// pointer event, which is always between frames.
///
/// See also:
///
/// * [Listener.onPointerHover], which does the same job. Prefer using
/// [Listener.onPointerHover], since hover events are similar to other regular
/// events.
final PointerHoverEventListener? onHover;
/// Triggered when a mouse pointer has exited this widget when the widget is
......
......@@ -105,7 +105,10 @@ void main() {
_binding.callback = events.add;
ui.window.onPointerDataPacket(packet);
expect(events.length, 0);
expect(events.length, 3);
expect(events[0], isA<PointerHoverEvent>());
expect(events[1], isA<PointerHoverEvent>());
expect(events[2], isA<PointerHoverEvent>());
expect(pointerRouterEvents.length, 6,
reason: 'pointerRouterEvents contains: $pointerRouterEvents');
expect(pointerRouterEvents[0], isA<PointerAddedEvent>());
......
......@@ -546,7 +546,7 @@ void main() {
ui.window.onPointerDataPacket(ui.PointerDataPacket(data: <ui.PointerData>[
_pointerData(PointerChange.hover, const Offset(0.0, 10.0)),
]));
expect(logs, <String>['enterA', 'enterB', 'hoverA', 'hoverB']);
expect(logs, <String>['enterA', 'enterB', 'hoverB', 'hoverA']);
logs.clear();
// Moves out of A within one frame.
......
......@@ -77,7 +77,6 @@ class TestAnnotationTarget with Diagnosticable implements MouseTrackerAnnotation
@override
final PointerEnterEventListener? onEnter;
@override
final PointerHoverEventListener? onHover;
@override
......
......@@ -87,6 +87,19 @@ void main() {
expect(fakePlatformViewController.dispatchedPointerEvents, isNotEmpty);
});
test('touch hover events are dispatched via PlatformViewController.dispatchPointerEvent', () {
layout(platformViewRenderBox);
pumpFrame(phase: EnginePhase.flushSemantics);
ui.window.onPointerDataPacket(ui.PointerDataPacket(data: <ui.PointerData>[
_pointerData(ui.PointerChange.add, const Offset(0, 0)),
_pointerData(ui.PointerChange.hover, const Offset(10, 10)),
_pointerData(ui.PointerChange.remove, const Offset(10, 10)),
]));
expect(fakePlatformViewController.dispatchedPointerEvents, isNotEmpty);
});
}, skip: isBrowser); // TODO(yjbanov): fails on Web with obscured stack trace: https://github.com/flutter/flutter/issues/42770
}
......
......@@ -49,6 +49,34 @@ void main() {
]));
});
testWidgets('Detects hover events from touch devices', (WidgetTester tester) async {
final List<String> log = <String>[];
await tester.pumpWidget(
Center(
child: SizedBox(
width: 300,
height: 300,
child: Listener(
onPointerHover: (_) {
log.add('bottom');
},
child: const Text('X', textDirection: TextDirection.ltr),
),
),
),
);
final TestGesture gesture = await tester.createGesture();
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(tester.getCenter(find.byType(Listener)));
expect(log, equals(<String>[
'bottom',
]));
});
group('transformed events', () {
testWidgets('simple offset for touch/signal', (WidgetTester tester) async {
final List<PointerEvent> events = <PointerEvent>[];
......
......@@ -125,7 +125,8 @@ void main() {
PointerExitEvent exit;
await tester.pumpWidget(Center(
child: MouseRegion(
child: const SizedBox(
child: Container(
color: const Color.fromARGB(0xff, 0xff, 0x00, 0x00),
width: 100.0,
height: 100.0,
),
......@@ -372,6 +373,37 @@ void main() {
expect(exit.localPosition, equals(const Offset(50, 50)));
});
testWidgets('detects hover from touch devices', (WidgetTester tester) async {
PointerEnterEvent enter;
PointerHoverEvent move;
PointerExitEvent exit;
await tester.pumpWidget(Center(
child: MouseRegion(
child: Container(
color: const Color.fromARGB(0xff, 0xff, 0x00, 0x00),
width: 100.0,
height: 100.0,
),
onEnter: (PointerEnterEvent details) => enter = details,
onHover: (PointerHoverEvent details) => move = details,
onExit: (PointerExitEvent details) => exit = details,
),
));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.touch);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await tester.pump();
move = null;
enter = null;
exit = null;
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, isNull);
expect(exit, isNull);
});
testWidgets('Hover works with nested listeners', (WidgetTester tester) async {
final UniqueKey key1 = UniqueKey();
final UniqueKey key2 = UniqueKey();
......@@ -589,8 +621,12 @@ void main() {
}
await tester.pumpWidget(hoverableContainer(
onEnter: (PointerEnterEvent details) { logs.add('enter1'); },
onHover: (PointerHoverEvent details) { logs.add('hover1'); },
onEnter: (PointerEnterEvent details) {
logs.add('enter1');
},
onHover: (PointerHoverEvent details) {
logs.add('hover1');
},
onExit: (PointerExitEvent details) { logs.add('exit1'); },
));
......@@ -1178,31 +1214,31 @@ void main() {
// Move to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterB', 'enterC', 'hoverA', 'hoverB', 'hoverC']);
expect(logs, <String>['enterA', 'enterB', 'enterC', 'hoverC', 'hoverB', 'hoverA']);
logs.clear();
// Move to the B only area.
await gesture.moveTo(const Offset(25, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitC', 'hoverA', 'hoverB']);
expect(logs, <String>['exitC', 'hoverB', 'hoverA']);
logs.clear();
// Move back to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterC', 'hoverA', 'hoverB', 'hoverC']);
expect(logs, <String>['enterC', 'hoverC', 'hoverB', 'hoverA']);
logs.clear();
// Move to the C only area.
await gesture.moveTo(const Offset(125, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitB', 'hoverA', 'hoverC']);
expect(logs, <String>['exitB', 'hoverC', 'hoverA']);
logs.clear();
// Move back to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterB', 'hoverA', 'hoverB', 'hoverC']);
expect(logs, <String>['enterB', 'hoverC', 'hoverB', 'hoverA']);
logs.clear();
// Move out.
......@@ -1226,31 +1262,31 @@ void main() {
// Move to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterC', 'hoverA', 'hoverC']);
expect(logs, <String>['enterA', 'enterC', 'hoverC', 'hoverA']);
logs.clear();
// Move to the B only area.
await gesture.moveTo(const Offset(25, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitC', 'enterB', 'hoverA', 'hoverB']);
expect(logs, <String>['exitC', 'enterB', 'hoverB', 'hoverA']);
logs.clear();
// Move back to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['exitB', 'enterC', 'hoverA', 'hoverC']);
expect(logs, <String>['exitB', 'enterC', 'hoverC', 'hoverA']);
logs.clear();
// Move to the C only area.
await gesture.moveTo(const Offset(125, 75));
await tester.pumpAndSettle();
expect(logs, <String>['hoverA', 'hoverC']);
expect(logs, <String>['hoverC', 'hoverA']);
logs.clear();
// Move back to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['hoverA', 'hoverC']);
expect(logs, <String>['hoverC', 'hoverA']);
logs.clear();
// Move out.
......@@ -1274,7 +1310,7 @@ void main() {
// Move to the overlapping area.
await gesture.moveTo(const Offset(75, 75));
await tester.pumpAndSettle();
expect(logs, <String>['enterA', 'enterC', 'hoverA', 'hoverC']);
expect(logs, <String>['enterA', 'enterC', 'hoverC', 'hoverA']);
logs.clear();
// Move out.
......
......@@ -25,11 +25,7 @@ class TestPointer {
this.kind = PointerDeviceKind.touch,
int? device,
int buttons = kPrimaryButton,
])
: assert(kind != null),
assert(pointer != null),
assert(buttons != null),
_buttons = buttons {
]) : _buttons = buttons {
switch (kind) {
case PointerDeviceKind.mouse:
_device = device ?? 1;
......@@ -211,7 +207,6 @@ class TestPointer {
Duration timeStamp = Duration.zero,
Offset? location,
}) {
assert(timeStamp != null);
_location = location ?? _location;
return PointerAddedEvent(
timeStamp: timeStamp,
......@@ -230,7 +225,6 @@ class TestPointer {
Duration timeStamp = Duration.zero,
Offset? location,
}) {
assert(timeStamp != null);
_location = location ?? _location;
return PointerRemovedEvent(
timeStamp: timeStamp,
......@@ -251,13 +245,10 @@ class TestPointer {
Offset newLocation, {
Duration timeStamp = Duration.zero,
}) {
assert(newLocation != null);
assert(timeStamp != null);
assert(
!isDown,
'Hover events can only be generated when the pointer is up. To '
'simulate movement when the pointer is down, use move() instead.');
assert(kind != PointerDeviceKind.touch, "Touch pointers can't generate hover events");
final Offset delta = location != null ? newLocation - location! : Offset.zero;
_location = newLocation;
return PointerHoverEvent(
......@@ -278,8 +269,6 @@ class TestPointer {
Offset scrollDelta, {
Duration timeStamp = Duration.zero,
}) {
assert(scrollDelta != null);
assert(timeStamp != null);
assert(kind != PointerDeviceKind.touch, "Touch pointers can't generate pointer signal events");
assert(location != null);
return PointerScrollEvent(
......@@ -327,11 +316,7 @@ class TestGesture {
PointerDeviceKind kind = PointerDeviceKind.touch,
int? device,
int buttons = kPrimaryButton,
}) : assert(dispatcher != null),
assert(pointer != null),
assert(kind != null),
assert(buttons != null),
_dispatcher = dispatcher,
}) : _dispatcher = dispatcher,
_pointer = TestPointer(pointer, kind, device, buttons);
/// Dispatch a pointer down event at the given `downLocation`, caching the
......@@ -380,8 +365,7 @@ class TestGesture {
/// Send a move event moving the pointer by the given offset.
///
/// If the pointer is down, then a move event is dispatched. If the pointer is
/// up, then a hover event is dispatched. Touch devices are not able to send
/// hover events.
/// up, then a hover event is dispatched.
Future<void> moveBy(Offset offset, { Duration timeStamp = Duration.zero }) {
assert(_pointer.location != null);
return moveTo(_pointer.location! + offset, timeStamp: timeStamp);
......@@ -390,15 +374,12 @@ class TestGesture {
/// Send a move event moving the pointer to the given location.
///
/// If the pointer is down, then a move event is dispatched. If the pointer is
/// up, then a hover event is dispatched. Touch devices are not able to send
/// hover events.
/// up, then a hover event is dispatched.
Future<void> moveTo(Offset location, { Duration timeStamp = Duration.zero }) {
return TestAsyncUtils.guard<void>(() {
if (_pointer._isDown) {
return _dispatcher(_pointer.move(location, timeStamp: timeStamp));
} else {
assert(_pointer.kind != PointerDeviceKind.touch,
'Touch device move events can only be sent if the pointer is down.');
return _dispatcher(_pointer.hover(location, timeStamp: timeStamp));
}
});
......
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