mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add support for pointer hover (#6884)
This commit is contained in:
parent
15fb5c4ca6
commit
a4a783b64f
@ -1 +1 @@
|
||||
ce8b187914f599e8e579fab829671fb2f07064b7
|
||||
d1bc4c4850ee155430b3ff66609a225364048257
|
||||
|
@ -79,15 +79,15 @@ void beginFrame(Duration timeStamp) {
|
||||
void handlePointerDataPacket(ui.PointerDataPacket packet) {
|
||||
// The pointer packet contains a number of pointer movements, which we iterate
|
||||
// through and process.
|
||||
for (ui.PointerData pointer in packet.pointers) {
|
||||
if (pointer.change == ui.PointerChange.down) {
|
||||
for (ui.PointerData datum in packet.data) {
|
||||
if (datum.change == ui.PointerChange.down) {
|
||||
// If the pointer went down, we change the color of the circle to blue.
|
||||
color = const ui.Color(0xFF0000FF);
|
||||
// Rather than calling paint() synchronously, we ask the engine to
|
||||
// schedule a frame. The engine will call onBeginFrame when it is actually
|
||||
// time to produce the frame.
|
||||
ui.window.scheduleFrame();
|
||||
} else if (pointer.change == ui.PointerChange.up) {
|
||||
} else if (datum.change == ui.PointerChange.up) {
|
||||
// Similarly, if the pointer went up, we change the color of the circle to
|
||||
// green and schedule a frame. It's harmless to call scheduleFrame many
|
||||
// times because the engine will ignore redundant requests up until the
|
||||
|
@ -29,7 +29,7 @@ abstract class GestureBinding extends BindingBase implements HitTestable, HitTes
|
||||
static GestureBinding _instance;
|
||||
|
||||
void _handlePointerDataPacket(ui.PointerDataPacket packet) {
|
||||
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.pointers, ui.window.devicePixelRatio));
|
||||
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, ui.window.devicePixelRatio));
|
||||
_flushPointerEventQueue();
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,13 @@ class PointerEventConverter {
|
||||
// Map from platform pointer identifiers to PointerEvent pointer identifiers.
|
||||
static Map<int, _PointerState> _pointers = <int, _PointerState>{};
|
||||
|
||||
static _PointerState _ensureStateForPointer(ui.PointerData datum, Point position) {
|
||||
return _pointers.putIfAbsent(
|
||||
datum.device,
|
||||
() => new _PointerState(position)
|
||||
);
|
||||
}
|
||||
|
||||
/// Expand the given packet of pointer data into a sequence of framework pointer events.
|
||||
static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* {
|
||||
for (ui.PointerData datum in data) {
|
||||
@ -43,19 +50,14 @@ class PointerEventConverter {
|
||||
final Duration timeStamp = datum.timeStamp;
|
||||
final PointerDeviceKind kind = datum.kind;
|
||||
switch (datum.change) {
|
||||
case ui.PointerChange.down:
|
||||
assert(!_pointers.containsKey(datum.pointer));
|
||||
_PointerState state = _pointers.putIfAbsent(
|
||||
datum.pointer,
|
||||
() => new _PointerState(position)
|
||||
);
|
||||
case ui.PointerChange.add:
|
||||
assert(!_pointers.containsKey(datum.device));
|
||||
_PointerState state = _ensureStateForPointer(datum, position);
|
||||
assert(state.lastPosition == position);
|
||||
state.startNewPointer();
|
||||
state.setDown();
|
||||
yield new PointerAddedEvent(
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
@ -67,10 +69,108 @@ class PointerEventConverter {
|
||||
orientation: datum.orientation,
|
||||
tilt: datum.tilt
|
||||
);
|
||||
break;
|
||||
case ui.PointerChange.hover:
|
||||
final bool alreadyAdded = _pointers.containsKey(datum.device);
|
||||
_PointerState state = _ensureStateForPointer(datum, position);
|
||||
assert(!state.down);
|
||||
if (!alreadyAdded) {
|
||||
assert(state.lastPosition == position);
|
||||
yield new PointerAddedEvent(
|
||||
timeStamp: timeStamp,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMin: datum.radiusMin,
|
||||
radiusMax: datum.radiusMax,
|
||||
orientation: datum.orientation,
|
||||
tilt: datum.tilt
|
||||
);
|
||||
}
|
||||
Offset offset = position - state.lastPosition;
|
||||
state.lastPosition = position;
|
||||
yield new PointerHoverEvent(
|
||||
timeStamp: timeStamp,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
delta: offset,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMajor: datum.radiusMajor,
|
||||
radiusMinor: datum.radiusMajor,
|
||||
radiusMin: datum.radiusMin,
|
||||
radiusMax: datum.radiusMax,
|
||||
orientation: datum.orientation,
|
||||
tilt: datum.tilt
|
||||
);
|
||||
state.lastPosition = position;
|
||||
break;
|
||||
case ui.PointerChange.down:
|
||||
final bool alreadyAdded = _pointers.containsKey(datum.device);
|
||||
_PointerState state = _ensureStateForPointer(datum, position);
|
||||
assert(!state.down);
|
||||
if (!alreadyAdded) {
|
||||
assert(state.lastPosition == position);
|
||||
yield new PointerAddedEvent(
|
||||
timeStamp: timeStamp,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMin: datum.radiusMin,
|
||||
radiusMax: datum.radiusMax,
|
||||
orientation: datum.orientation,
|
||||
tilt: datum.tilt
|
||||
);
|
||||
}
|
||||
if (state.lastPosition != position) {
|
||||
// Not all sources of pointer packets respect the invariant that
|
||||
// they hover the pointer to the down location before sending the
|
||||
// down event. We restore the invariant here for our clients.
|
||||
Offset offset = position - state.lastPosition;
|
||||
state.lastPosition = position;
|
||||
yield new PointerHoverEvent(
|
||||
timeStamp: timeStamp,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
delta: offset,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMajor: datum.radiusMajor,
|
||||
radiusMinor: datum.radiusMajor,
|
||||
radiusMin: datum.radiusMin,
|
||||
radiusMax: datum.radiusMax,
|
||||
orientation: datum.orientation,
|
||||
tilt: datum.tilt
|
||||
);
|
||||
state.lastPosition = position;
|
||||
}
|
||||
state.startNewPointer();
|
||||
state.setDown();
|
||||
yield new PointerDownEvent(
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
@ -90,8 +190,8 @@ class PointerEventConverter {
|
||||
// If the service starts supporting hover pointers, then it must also
|
||||
// start sending us ADDED and REMOVED data points.
|
||||
// See also: https://github.com/flutter/flutter/issues/720
|
||||
assert(_pointers.containsKey(datum.pointer));
|
||||
_PointerState state = _pointers[datum.pointer];
|
||||
assert(_pointers.containsKey(datum.device));
|
||||
_PointerState state = _pointers[datum.device];
|
||||
assert(state.down);
|
||||
Offset offset = position - state.lastPosition;
|
||||
state.lastPosition = position;
|
||||
@ -99,15 +199,14 @@ class PointerEventConverter {
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
delta: offset,
|
||||
down: state.down,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
pressure: datum.pressure,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMajor: datum.radiusMajor,
|
||||
radiusMinor: datum.radiusMajor,
|
||||
@ -119,8 +218,8 @@ class PointerEventConverter {
|
||||
break;
|
||||
case ui.PointerChange.up:
|
||||
case ui.PointerChange.cancel:
|
||||
assert(_pointers.containsKey(datum.pointer));
|
||||
_PointerState state = _pointers[datum.pointer];
|
||||
assert(_pointers.containsKey(datum.device));
|
||||
_PointerState state = _pointers[datum.device];
|
||||
assert(state.down);
|
||||
if (position != state.lastPosition) {
|
||||
// Not all sources of pointer packets respect the invariant that
|
||||
@ -134,15 +233,14 @@ class PointerEventConverter {
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
delta: offset,
|
||||
down: state.down,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
pressure: datum.pressure,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
distance: datum.distance,
|
||||
distanceMax: datum.distanceMax,
|
||||
radiusMajor: datum.radiusMajor,
|
||||
radiusMinor: datum.radiusMajor,
|
||||
@ -160,6 +258,7 @@ class PointerEventConverter {
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
@ -176,6 +275,7 @@ class PointerEventConverter {
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
position: position,
|
||||
buttons: datum.buttons,
|
||||
obscured: datum.obscured,
|
||||
@ -189,10 +289,13 @@ class PointerEventConverter {
|
||||
tilt: datum.tilt
|
||||
);
|
||||
}
|
||||
_pointers.remove(datum.device);
|
||||
break;
|
||||
case ui.PointerChange.remove:
|
||||
yield new PointerRemovedEvent(
|
||||
timeStamp: timeStamp,
|
||||
pointer: state.pointer,
|
||||
kind: kind,
|
||||
device: datum.device,
|
||||
obscured: datum.obscured,
|
||||
pressureMin: datum.pressureMin,
|
||||
pressureMax: datum.pressureMax,
|
||||
@ -200,7 +303,6 @@ class PointerEventConverter {
|
||||
radiusMin: datum.radiusMin,
|
||||
radiusMax: datum.radiusMax
|
||||
);
|
||||
_pointers.remove(datum.pointer);
|
||||
break;
|
||||
default:
|
||||
// TODO(ianh): once https://github.com/flutter/flutter/issues/720 is
|
||||
|
@ -76,6 +76,7 @@ abstract class PointerEvent {
|
||||
this.timeStamp: Duration.ZERO,
|
||||
this.pointer: 0,
|
||||
this.kind: PointerDeviceKind.touch,
|
||||
this.device: 0,
|
||||
this.position: Point.origin,
|
||||
this.delta: Offset.zero,
|
||||
this.buttons: 0,
|
||||
@ -103,6 +104,9 @@ abstract class PointerEvent {
|
||||
/// The kind of input device for which the event was generated.
|
||||
final PointerDeviceKind kind;
|
||||
|
||||
/// Unique identifier for the pointing device, reused across interactions.
|
||||
final int device;
|
||||
|
||||
/// Coordinate of the position of the pointer, in logical pixels in the global
|
||||
/// coordinate space.
|
||||
final Point position;
|
||||
@ -219,6 +223,7 @@ abstract class PointerEvent {
|
||||
'timeStamp: $timeStamp, '
|
||||
'pointer: $pointer, '
|
||||
'kind: $kind, '
|
||||
'device: $device, '
|
||||
'position: $position, '
|
||||
'delta: $delta, '
|
||||
'buttons: $buttons, '
|
||||
@ -250,8 +255,8 @@ class PointerAddedEvent extends PointerEvent {
|
||||
/// All of the argument must be non-null.
|
||||
const PointerAddedEvent({
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
bool obscured: false,
|
||||
double pressureMin: 1.0,
|
||||
@ -264,8 +269,8 @@ class PointerAddedEvent extends PointerEvent {
|
||||
double tilt: 0.0
|
||||
}) : super(
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
obscured: obscured,
|
||||
pressureMin: pressureMin,
|
||||
@ -289,8 +294,8 @@ class PointerRemovedEvent extends PointerEvent {
|
||||
/// All of the argument must be non-null.
|
||||
const PointerRemovedEvent({
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
bool obscured: false,
|
||||
double pressureMin: 1.0,
|
||||
double pressureMax: 1.0,
|
||||
@ -299,8 +304,8 @@ class PointerRemovedEvent extends PointerEvent {
|
||||
double radiusMax: 0.0
|
||||
}) : super(
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: null,
|
||||
obscured: obscured,
|
||||
pressureMin: pressureMin,
|
||||
@ -311,6 +316,57 @@ class PointerRemovedEvent extends PointerEvent {
|
||||
);
|
||||
}
|
||||
|
||||
/// The pointer has moved with respect to the device while the pointer is not
|
||||
/// in contact with the device.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [PointerMoveEvent], which reports movement while the pointer is in
|
||||
/// contact with the device.
|
||||
class PointerHoverEvent extends PointerEvent {
|
||||
/// Creates a pointer hover event.
|
||||
///
|
||||
/// All of the argument must be non-null.
|
||||
const PointerHoverEvent({
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
Offset delta: Offset.zero,
|
||||
int buttons: 0,
|
||||
bool obscured: false,
|
||||
double pressureMin: 1.0,
|
||||
double pressureMax: 1.0,
|
||||
double distance: 0.0,
|
||||
double distanceMax: 0.0,
|
||||
double radiusMajor: 0.0,
|
||||
double radiusMinor: 0.0,
|
||||
double radiusMin: 0.0,
|
||||
double radiusMax: 0.0,
|
||||
double orientation: 0.0,
|
||||
double tilt: 0.0
|
||||
}) : super(
|
||||
timeStamp: timeStamp,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
delta: delta,
|
||||
buttons: buttons,
|
||||
down: false,
|
||||
obscured: obscured,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distanceMax: distanceMax,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
radiusMin: radiusMin,
|
||||
radiusMax: radiusMax,
|
||||
orientation: orientation,
|
||||
tilt: tilt
|
||||
);
|
||||
}
|
||||
|
||||
/// The pointer has made contact with the device.
|
||||
class PointerDownEvent extends PointerEvent {
|
||||
/// Creates a pointer down event.
|
||||
@ -320,6 +376,7 @@ class PointerDownEvent extends PointerEvent {
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
int buttons: 0,
|
||||
bool obscured: false,
|
||||
@ -337,6 +394,7 @@ class PointerDownEvent extends PointerEvent {
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
buttons: buttons,
|
||||
down: true,
|
||||
@ -355,7 +413,13 @@ class PointerDownEvent extends PointerEvent {
|
||||
);
|
||||
}
|
||||
|
||||
/// The pointer has moved with respect to the device.
|
||||
/// The pointer has moved with respect to the device while the pointer is in
|
||||
/// contact with the device.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [PointerHoverEvent], which reports movement while the pointer is not in
|
||||
/// contact with the device.
|
||||
class PointerMoveEvent extends PointerEvent {
|
||||
/// Creates a pointer move event.
|
||||
///
|
||||
@ -364,15 +428,14 @@ class PointerMoveEvent extends PointerEvent {
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
Offset delta: Offset.zero,
|
||||
int buttons: 0,
|
||||
bool down: false,
|
||||
bool obscured: false,
|
||||
double pressure: 1.0,
|
||||
double pressureMin: 1.0,
|
||||
double pressureMax: 1.0,
|
||||
double distance: 0.0,
|
||||
double distanceMax: 0.0,
|
||||
double radiusMajor: 0.0,
|
||||
double radiusMinor: 0.0,
|
||||
@ -384,15 +447,16 @@ class PointerMoveEvent extends PointerEvent {
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
delta: delta,
|
||||
buttons: buttons,
|
||||
down: down,
|
||||
down: true,
|
||||
obscured: obscured,
|
||||
pressure: pressure,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
distance: distance,
|
||||
distance: 0.0,
|
||||
distanceMax: distanceMax,
|
||||
radiusMajor: radiusMajor,
|
||||
radiusMinor: radiusMinor,
|
||||
@ -412,6 +476,7 @@ class PointerUpEvent extends PointerEvent {
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
int buttons: 0,
|
||||
bool obscured: false,
|
||||
@ -427,8 +492,10 @@ class PointerUpEvent extends PointerEvent {
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
buttons: buttons,
|
||||
down: false,
|
||||
obscured: obscured,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
@ -450,6 +517,7 @@ class PointerCancelEvent extends PointerEvent {
|
||||
Duration timeStamp: Duration.ZERO,
|
||||
int pointer: 0,
|
||||
PointerDeviceKind kind: PointerDeviceKind.touch,
|
||||
int device: 0,
|
||||
Point position: Point.origin,
|
||||
int buttons: 0,
|
||||
bool obscured: false,
|
||||
@ -465,8 +533,10 @@ class PointerCancelEvent extends PointerEvent {
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
kind: kind,
|
||||
device: device,
|
||||
position: position,
|
||||
buttons: buttons,
|
||||
down: false,
|
||||
obscured: obscured,
|
||||
pressureMin: pressureMin,
|
||||
pressureMax: pressureMax,
|
||||
|
@ -34,7 +34,7 @@ void main() {
|
||||
|
||||
test('Pointer tap events', () {
|
||||
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||
pointers: <ui.PointerData>[
|
||||
data: <ui.PointerData>[
|
||||
new ui.PointerData(change: ui.PointerChange.down),
|
||||
new ui.PointerData(change: ui.PointerChange.up),
|
||||
]
|
||||
@ -51,7 +51,7 @@ void main() {
|
||||
|
||||
test('Pointer move events', () {
|
||||
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||
pointers: <ui.PointerData>[
|
||||
data: <ui.PointerData>[
|
||||
new ui.PointerData(change: ui.PointerChange.down),
|
||||
new ui.PointerData(change: ui.PointerChange.move),
|
||||
new ui.PointerData(change: ui.PointerChange.up),
|
||||
@ -70,7 +70,7 @@ void main() {
|
||||
|
||||
test('Synthetic move events', () {
|
||||
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||
pointers: <ui.PointerData>[
|
||||
data: <ui.PointerData>[
|
||||
new ui.PointerData(
|
||||
change: ui.PointerChange.down,
|
||||
physicalX: 1.0,
|
||||
@ -97,7 +97,7 @@ void main() {
|
||||
|
||||
test('Pointer cancel events', () {
|
||||
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||
pointers: <ui.PointerData>[
|
||||
data: <ui.PointerData>[
|
||||
new ui.PointerData(change: ui.PointerChange.down),
|
||||
new ui.PointerData(change: ui.PointerChange.cancel),
|
||||
]
|
||||
@ -114,7 +114,7 @@ void main() {
|
||||
|
||||
test('Can cancel pointers', () {
|
||||
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||
pointers: <ui.PointerData>[
|
||||
data: <ui.PointerData>[
|
||||
new ui.PointerData(change: ui.PointerChange.down),
|
||||
new ui.PointerData(change: ui.PointerChange.up),
|
||||
]
|
||||
|
@ -66,7 +66,6 @@ class TestPointer {
|
||||
return new PointerMoveEvent(
|
||||
timeStamp: timeStamp,
|
||||
pointer: pointer,
|
||||
down: _isDown,
|
||||
position: newLocation,
|
||||
delta: delta
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user