// Copyright 2015 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. import 'package:flutter/gestures.dart'; export 'dart:ui' show Point; /// A class for generating coherent artificial pointer events. /// /// You can use this to manually simulate individual events, but the /// simplest way to generate coherent gestures is to use [TestGesture]. class TestPointer { /// Creates a [TestPointer]. By default, the pointer identifier used is 1, however /// this can be overridden by providing an argument to the constructor. TestPointer([ this.pointer = 1 ]); /// The pointer identifier used for events generated by this object. /// /// Set when the object is constructed. Defaults to 1. final int pointer; /// Whether the pointer simulated by this object is currently down. /// /// A pointer is released (goes up) by calling [up] or [cancel]. /// /// Once a pointer is released, it can no longer generate events. bool get isDown => _isDown; bool _isDown = false; /// The position of the last event sent by this object. /// /// If no event has ever been sent by this object, returns null. Point get location => _location; Point _location; /// Create a [PointerDownEvent] at the given location. /// /// By default, the time stamp on the event is [Duration.ZERO]. You /// can give a specific time stamp by passing the `timeStamp` /// argument. PointerDownEvent down(Point newLocation, { Duration timeStamp: Duration.ZERO }) { assert(!isDown); _isDown = true; _location = newLocation; return new PointerDownEvent( timeStamp: timeStamp, pointer: pointer, position: location ); } /// Create a [PointerMoveEvent] to the given location. /// /// By default, the time stamp on the event is [Duration.ZERO]. You /// can give a specific time stamp by passing the `timeStamp` /// argument. PointerMoveEvent move(Point newLocation, { Duration timeStamp: Duration.ZERO }) { assert(isDown); Offset delta = newLocation - location; _location = newLocation; return new PointerMoveEvent( timeStamp: timeStamp, pointer: pointer, position: newLocation, delta: delta ); } /// Create a [PointerUpEvent]. /// /// By default, the time stamp on the event is [Duration.ZERO]. You /// can give a specific time stamp by passing the `timeStamp` /// argument. /// /// The object is no longer usable after this method has been called. PointerUpEvent up({ Duration timeStamp: Duration.ZERO }) { assert(isDown); _isDown = false; return new PointerUpEvent( timeStamp: timeStamp, pointer: pointer, position: location ); } /// Create a [PointerCancelEvent]. /// /// By default, the time stamp on the event is [Duration.ZERO]. You /// can give a specific time stamp by passing the `timeStamp` /// argument. /// /// The object is no longer usable after this method has been called. PointerCancelEvent cancel({ Duration timeStamp: Duration.ZERO }) { assert(isDown); _isDown = false; return new PointerCancelEvent( timeStamp: timeStamp, pointer: pointer, position: location ); } } /// A class for performing gestures in tests. /// /// The simplest way to create a [TestGesture] is to call /// [WidgetTester.startGesture]. class TestGesture { /// Create a [TestGesture] by starting with a pointerDown at the /// given point. /// /// By default, the pointer ID used is 1. This can be overridden by /// providing the `pointer` argument. /// /// By default, the global binding is used both for hit testing and /// for dispatching of events. The object to use for hit testing can /// be overridden by providing `hitTestTarget`, and the object to /// use for dispatching events can be overridden by providing an /// `dispatcher`. factory TestGesture(Point downLocation, { int pointer: 1, HitTestable target, HitTestDispatcher dispatcher }) { // hit test final HitTestResult result = new HitTestResult(); target ??= GestureBinding.instance; assert(target != null); target.hitTest(result, downLocation); // dispatch down event final TestPointer testPointer = new TestPointer(pointer); dispatcher ??= GestureBinding.instance; assert(dispatcher != null); dispatcher.dispatchEvent(testPointer.down(downLocation), result); // create a TestGesture return new TestGesture._(dispatcher, result, testPointer); } const TestGesture._(this._dispatcher, this._result, this._pointer); final HitTestDispatcher _dispatcher; final HitTestResult _result; final TestPointer _pointer; /// Send a move event moving the pointer by the given offset. void moveBy(Offset offset) { assert(_pointer._isDown); moveTo(_pointer.location + offset); } /// Send a move event moving the pointer to the given location. void moveTo(Point location) { assert(_pointer._isDown); _dispatcher.dispatchEvent(_pointer.move(location), _result); } /// End the gesture by releasing the pointer. /// /// The object is no longer usable after this method has been called. void up() { assert(_pointer._isDown); _dispatcher.dispatchEvent(_pointer.up(), _result); assert(!_pointer._isDown); } /// End the gesture by canceling the pointer (as would happen if the /// system showed a modal dialog on top of the Flutter application, /// for instance). /// /// The object is no longer usable after this method has been called. void cancel() { assert(_pointer._isDown); _dispatcher.dispatchEvent(_pointer.cancel(), _result); assert(!_pointer._isDown); } }