Switch to PointerData API (#6131)
Now dart:ui does the decoding of the pointer data itself, which means we don't need to do it in the framework.
This commit is contained in:
parent
2a530f78d4
commit
c9eda86b0e
@ -8,10 +8,6 @@
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:mojo/bindings.dart' as bindings;
|
|
||||||
import 'package:mojo/core.dart' as core;
|
|
||||||
import 'package:flutter_services/pointer.dart';
|
|
||||||
|
|
||||||
ui.Color color;
|
ui.Color color;
|
||||||
|
|
||||||
ui.Picture paint(ui.Rect paintBounds) {
|
ui.Picture paint(ui.Rect paintBounds) {
|
||||||
@ -80,26 +76,18 @@ void beginFrame(Duration timeStamp) {
|
|||||||
ui.window.render(scene);
|
ui.window.render(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pointer input arrives as an array of bytes. The format for the data is
|
void handlePointerDataPacket(ui.PointerDataPacket packet) {
|
||||||
// defined by pointer.mojom, which generates serializes and parsers for a
|
// The pointer packet contains a number of pointer movements, which we iterate
|
||||||
// number of languages, including Dart, C++, Java, and Go.
|
// through and process.
|
||||||
void handlePointerPacket(ByteData serializedPacket) {
|
for (ui.PointerData pointer in packet.pointers) {
|
||||||
// We wrap the byte data up into a Mojo Message object, which we then
|
if (pointer.change == ui.PointerChange.down) {
|
||||||
// deserialize according to the mojom definition.
|
|
||||||
bindings.Message message = new bindings.Message(serializedPacket, <core.MojoHandle>[], serializedPacket.lengthInBytes, 0);
|
|
||||||
PointerPacket packet = PointerPacket.deserialize(message);
|
|
||||||
|
|
||||||
// The deserialized pointer packet contains a number of pointer movements,
|
|
||||||
// which we iterate through and process.
|
|
||||||
for (Pointer pointer in packet.pointers) {
|
|
||||||
if (pointer.type == PointerType.down) {
|
|
||||||
// If the pointer went down, we change the color of the circle to blue.
|
// If the pointer went down, we change the color of the circle to blue.
|
||||||
color = const ui.Color(0xFF0000FF);
|
color = const ui.Color(0xFF0000FF);
|
||||||
// Rather than calling paint() synchronously, we ask the engine to
|
// Rather than calling paint() synchronously, we ask the engine to
|
||||||
// schedule a frame. The engine will call onBeginFrame when it is actually
|
// schedule a frame. The engine will call onBeginFrame when it is actually
|
||||||
// time to produce the frame.
|
// time to produce the frame.
|
||||||
ui.window.scheduleFrame();
|
ui.window.scheduleFrame();
|
||||||
} else if (pointer.type == PointerType.up) {
|
} else if (pointer.change == ui.PointerChange.up) {
|
||||||
// Similarly, if the pointer went up, we change the color of the circle to
|
// 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
|
// green and schedule a frame. It's harmless to call scheduleFrame many
|
||||||
// times because the engine will ignore redundant requests up until the
|
// times because the engine will ignore redundant requests up until the
|
||||||
@ -117,9 +105,9 @@ void main() {
|
|||||||
color = const ui.Color(0xFF00FF00);
|
color = const ui.Color(0xFF00FF00);
|
||||||
// The engine calls onBeginFrame whenever it wants us to produce a frame.
|
// The engine calls onBeginFrame whenever it wants us to produce a frame.
|
||||||
ui.window.onBeginFrame = beginFrame;
|
ui.window.onBeginFrame = beginFrame;
|
||||||
// The engine calls onPointerPacket whenever it had updated information about
|
// The engine calls onPointerDataPacket whenever it had updated information
|
||||||
// the pointers directed at our app.
|
// about the pointers directed at our app.
|
||||||
ui.window.onPointerPacket = handlePointerPacket;
|
ui.window.onPointerDataPacket = handlePointerDataPacket;
|
||||||
// Here we kick off the whole process by asking the engine to schedule a new
|
// Here we kick off the whole process by asking the engine to schedule a new
|
||||||
// frame. The engine will eventually call onBeginFrame when it is time for us
|
// frame. The engine will eventually call onBeginFrame when it is time for us
|
||||||
// to actually produce the frame.
|
// to actually produce the frame.
|
||||||
|
@ -4,13 +4,9 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:ui' as ui show window, PointerDataPacket;
|
||||||
import 'dart:ui' as ui show window;
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:mojo/bindings.dart' as mojo_bindings;
|
|
||||||
import 'package:mojo/core.dart' as mojo_core;
|
|
||||||
import 'package:flutter_services/pointer.dart';
|
|
||||||
|
|
||||||
import 'arena.dart';
|
import 'arena.dart';
|
||||||
import 'converter.dart';
|
import 'converter.dart';
|
||||||
@ -25,21 +21,14 @@ abstract class GestureBinding extends BindingBase implements HitTestable, HitTes
|
|||||||
void initInstances() {
|
void initInstances() {
|
||||||
super.initInstances();
|
super.initInstances();
|
||||||
_instance = this;
|
_instance = this;
|
||||||
ui.window.onPointerPacket = _handlePointerPacket;
|
ui.window.onPointerDataPacket = _handlePointerDataPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The singleton instance of this object.
|
/// The singleton instance of this object.
|
||||||
static GestureBinding get instance => _instance;
|
static GestureBinding get instance => _instance;
|
||||||
static GestureBinding _instance;
|
static GestureBinding _instance;
|
||||||
|
|
||||||
void _handlePointerPacket(ByteData serializedPacket) {
|
void _handlePointerDataPacket(ui.PointerDataPacket packet) {
|
||||||
final mojo_bindings.Message message = new mojo_bindings.Message(
|
|
||||||
serializedPacket,
|
|
||||||
<mojo_core.MojoHandle>[],
|
|
||||||
serializedPacket.lengthInBytes,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
final PointerPacket packet = PointerPacket.deserialize(message);
|
|
||||||
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.pointers, ui.window.devicePixelRatio));
|
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.pointers, ui.window.devicePixelRatio));
|
||||||
_flushPointerEventQueue();
|
_flushPointerEventQueue();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:flutter_services/pointer.dart' as mojom;
|
import 'dart:ui' as ui show PointerData, PointerChange;
|
||||||
|
|
||||||
import 'events.dart';
|
import 'events.dart';
|
||||||
|
|
||||||
@ -37,14 +37,13 @@ class PointerEventConverter {
|
|||||||
static Map<int, _PointerState> _pointers = <int, _PointerState>{};
|
static Map<int, _PointerState> _pointers = <int, _PointerState>{};
|
||||||
|
|
||||||
/// Expand the given packet of pointer data into a sequence of framework pointer events.
|
/// Expand the given packet of pointer data into a sequence of framework pointer events.
|
||||||
static Iterable<PointerEvent> expand(Iterable<mojom.Pointer> packet, double devicePixelRatio) sync* {
|
static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) sync* {
|
||||||
for (mojom.Pointer datum in packet) {
|
for (ui.PointerData datum in data) {
|
||||||
Point position = new Point(datum.x, datum.y) / devicePixelRatio;
|
final Point position = new Point(datum.physicalX, datum.physicalY) / devicePixelRatio;
|
||||||
Duration timeStamp = new Duration(microseconds: datum.timeStamp);
|
final Duration timeStamp = datum.timeStamp;
|
||||||
assert(_pointerKindMap.containsKey(datum.kind));
|
final PointerDeviceKind kind = datum.kind;
|
||||||
PointerDeviceKind kind = _pointerKindMap[datum.kind];
|
switch (datum.change) {
|
||||||
switch (datum.type) {
|
case ui.PointerChange.down:
|
||||||
case mojom.PointerType.down:
|
|
||||||
assert(!_pointers.containsKey(datum.pointer));
|
assert(!_pointers.containsKey(datum.pointer));
|
||||||
_PointerState state = _pointers.putIfAbsent(
|
_PointerState state = _pointers.putIfAbsent(
|
||||||
datum.pointer,
|
datum.pointer,
|
||||||
@ -87,7 +86,7 @@ class PointerEventConverter {
|
|||||||
tilt: datum.tilt
|
tilt: datum.tilt
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case mojom.PointerType.move:
|
case ui.PointerChange.move:
|
||||||
// If the service starts supporting hover pointers, then it must also
|
// If the service starts supporting hover pointers, then it must also
|
||||||
// start sending us ADDED and REMOVED data points.
|
// start sending us ADDED and REMOVED data points.
|
||||||
// See also: https://github.com/flutter/flutter/issues/720
|
// See also: https://github.com/flutter/flutter/issues/720
|
||||||
@ -118,8 +117,8 @@ class PointerEventConverter {
|
|||||||
tilt: datum.tilt
|
tilt: datum.tilt
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case mojom.PointerType.up:
|
case ui.PointerChange.up:
|
||||||
case mojom.PointerType.cancel:
|
case ui.PointerChange.cancel:
|
||||||
assert(_pointers.containsKey(datum.pointer));
|
assert(_pointers.containsKey(datum.pointer));
|
||||||
_PointerState state = _pointers[datum.pointer];
|
_PointerState state = _pointers[datum.pointer];
|
||||||
assert(state.down);
|
assert(state.down);
|
||||||
@ -156,7 +155,7 @@ class PointerEventConverter {
|
|||||||
}
|
}
|
||||||
assert(position == state.lastPosition);
|
assert(position == state.lastPosition);
|
||||||
state.setUp();
|
state.setUp();
|
||||||
if (datum.type == mojom.PointerType.up) {
|
if (datum.change == ui.PointerChange.up) {
|
||||||
yield new PointerUpEvent(
|
yield new PointerUpEvent(
|
||||||
timeStamp: timeStamp,
|
timeStamp: timeStamp,
|
||||||
pointer: state.pointer,
|
pointer: state.pointer,
|
||||||
@ -210,11 +209,4 @@ class PointerEventConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Map<mojom.PointerKind, PointerDeviceKind> _pointerKindMap = const <mojom.PointerKind, PointerDeviceKind>{
|
|
||||||
mojom.PointerKind.touch: PointerDeviceKind.touch,
|
|
||||||
mojom.PointerKind.mouse: PointerDeviceKind.mouse,
|
|
||||||
mojom.PointerKind.stylus: PointerDeviceKind.stylus,
|
|
||||||
mojom.PointerKind.invertedStylus: PointerDeviceKind.invertedStylus,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,11 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:ui' show Point, Offset;
|
import 'dart:ui' show Point, Offset, PointerDeviceKind;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
export 'dart:ui' show Point, Offset;
|
export 'dart:ui' show Point, Offset, PointerDeviceKind;
|
||||||
|
|
||||||
/// The kind of pointer device.
|
|
||||||
enum PointerDeviceKind {
|
|
||||||
/// A touch-based pointer device.
|
|
||||||
touch,
|
|
||||||
|
|
||||||
/// A pointer device with a stylus.
|
|
||||||
stylus,
|
|
||||||
|
|
||||||
/// A pointer device with a stylus that has been inverted.
|
|
||||||
invertedStylus,
|
|
||||||
|
|
||||||
/// A mouse-based pointer device.
|
|
||||||
mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The bit of [PointerEvent.buttons] that corresponds to the primary mouse button.
|
/// The bit of [PointerEvent.buttons] that corresponds to the primary mouse button.
|
||||||
///
|
///
|
||||||
|
@ -6,8 +6,6 @@ import 'dart:ui' as ui;
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:mojo/bindings.dart' as mojo_bindings;
|
|
||||||
import 'package:flutter_services/pointer.dart';
|
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
typedef void HandleEventCallback(PointerEvent event);
|
typedef void HandleEventCallback(PointerEvent event);
|
||||||
@ -35,42 +33,35 @@ void main() {
|
|||||||
setUp(ensureTestGestureBinding);
|
setUp(ensureTestGestureBinding);
|
||||||
|
|
||||||
test('Pointer tap events', () {
|
test('Pointer tap events', () {
|
||||||
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
|
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||||
|
pointers: <ui.PointerData>[
|
||||||
PointerPacket packet = new PointerPacket();
|
new ui.PointerData(change: ui.PointerChange.down),
|
||||||
packet.pointers = <Pointer>[new Pointer(), new Pointer()];
|
new ui.PointerData(change: ui.PointerChange.up),
|
||||||
packet.pointers[0].type = PointerType.down;
|
]
|
||||||
packet.pointers[0].kind = PointerKind.touch;
|
);
|
||||||
packet.pointers[1].type = PointerType.up;
|
|
||||||
packet.pointers[1].kind = PointerKind.touch;
|
|
||||||
packet.encode(encoder);
|
|
||||||
|
|
||||||
List<PointerEvent> events = <PointerEvent>[];
|
List<PointerEvent> events = <PointerEvent>[];
|
||||||
_binding.callback = (PointerEvent event) => events.add(event);
|
_binding.callback = (PointerEvent event) => events.add(event);
|
||||||
|
|
||||||
ui.window.onPointerPacket(encoder.message.buffer);
|
ui.window.onPointerDataPacket(packet);
|
||||||
expect(events.length, 2);
|
expect(events.length, 2);
|
||||||
expect(events[0].runtimeType, equals(PointerDownEvent));
|
expect(events[0].runtimeType, equals(PointerDownEvent));
|
||||||
expect(events[1].runtimeType, equals(PointerUpEvent));
|
expect(events[1].runtimeType, equals(PointerUpEvent));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Pointer move events', () {
|
test('Pointer move events', () {
|
||||||
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
|
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||||
|
pointers: <ui.PointerData>[
|
||||||
PointerPacket packet = new PointerPacket();
|
new ui.PointerData(change: ui.PointerChange.down),
|
||||||
packet.pointers = <Pointer>[new Pointer(), new Pointer(), new Pointer()];
|
new ui.PointerData(change: ui.PointerChange.move),
|
||||||
packet.pointers[0].type = PointerType.down;
|
new ui.PointerData(change: ui.PointerChange.up),
|
||||||
packet.pointers[0].kind = PointerKind.touch;
|
]
|
||||||
packet.pointers[1].type = PointerType.move;
|
);
|
||||||
packet.pointers[1].kind = PointerKind.touch;
|
|
||||||
packet.pointers[2].type = PointerType.up;
|
|
||||||
packet.pointers[2].kind = PointerKind.touch;
|
|
||||||
packet.encode(encoder);
|
|
||||||
|
|
||||||
List<PointerEvent> events = <PointerEvent>[];
|
List<PointerEvent> events = <PointerEvent>[];
|
||||||
_binding.callback = (PointerEvent event) => events.add(event);
|
_binding.callback = (PointerEvent event) => events.add(event);
|
||||||
|
|
||||||
ui.window.onPointerPacket(encoder.message.buffer);
|
ui.window.onPointerDataPacket(packet);
|
||||||
expect(events.length, 3);
|
expect(events.length, 3);
|
||||||
expect(events[0].runtimeType, equals(PointerDownEvent));
|
expect(events[0].runtimeType, equals(PointerDownEvent));
|
||||||
expect(events[1].runtimeType, equals(PointerMoveEvent));
|
expect(events[1].runtimeType, equals(PointerMoveEvent));
|
||||||
@ -78,26 +69,25 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Synthetic move events', () {
|
test('Synthetic move events', () {
|
||||||
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
|
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||||
|
pointers: <ui.PointerData>[
|
||||||
PointerPacket packet = new PointerPacket();
|
new ui.PointerData(
|
||||||
packet.pointers = <Pointer>[new Pointer(), new Pointer()];
|
change: ui.PointerChange.down,
|
||||||
packet.pointers[0]
|
physicalX: 1.0,
|
||||||
..type = PointerType.down
|
physicalY: 3.0,
|
||||||
..kind = PointerKind.touch
|
),
|
||||||
..x = 1.0
|
new ui.PointerData(
|
||||||
..y = 3.0;
|
change: ui.PointerChange.up,
|
||||||
packet.pointers[1]
|
physicalX: 10.0,
|
||||||
..type = PointerType.up
|
physicalY: 15.0,
|
||||||
..kind = PointerKind.touch
|
),
|
||||||
..x = 10.0
|
]
|
||||||
..y = 15.0;
|
);
|
||||||
packet.encode(encoder);
|
|
||||||
|
|
||||||
List<PointerEvent> events = <PointerEvent>[];
|
List<PointerEvent> events = <PointerEvent>[];
|
||||||
_binding.callback = (PointerEvent event) => events.add(event);
|
_binding.callback = (PointerEvent event) => events.add(event);
|
||||||
|
|
||||||
ui.window.onPointerPacket(encoder.message.buffer);
|
ui.window.onPointerDataPacket(packet);
|
||||||
expect(events.length, 3);
|
expect(events.length, 3);
|
||||||
expect(events[0].runtimeType, equals(PointerDownEvent));
|
expect(events[0].runtimeType, equals(PointerDownEvent));
|
||||||
expect(events[1].runtimeType, equals(PointerMoveEvent));
|
expect(events[1].runtimeType, equals(PointerMoveEvent));
|
||||||
@ -106,35 +96,29 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Pointer cancel events', () {
|
test('Pointer cancel events', () {
|
||||||
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
|
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||||
|
pointers: <ui.PointerData>[
|
||||||
PointerPacket packet = new PointerPacket();
|
new ui.PointerData(change: ui.PointerChange.down),
|
||||||
packet.pointers = <Pointer>[new Pointer(), new Pointer()];
|
new ui.PointerData(change: ui.PointerChange.cancel),
|
||||||
packet.pointers[0].type = PointerType.down;
|
]
|
||||||
packet.pointers[0].kind = PointerKind.touch;
|
);
|
||||||
packet.pointers[1].type = PointerType.cancel;
|
|
||||||
packet.pointers[1].kind = PointerKind.touch;
|
|
||||||
packet.encode(encoder);
|
|
||||||
|
|
||||||
List<PointerEvent> events = <PointerEvent>[];
|
List<PointerEvent> events = <PointerEvent>[];
|
||||||
_binding.callback = (PointerEvent event) => events.add(event);
|
_binding.callback = (PointerEvent event) => events.add(event);
|
||||||
|
|
||||||
ui.window.onPointerPacket(encoder.message.buffer);
|
ui.window.onPointerDataPacket(packet);
|
||||||
expect(events.length, 2);
|
expect(events.length, 2);
|
||||||
expect(events[0].runtimeType, equals(PointerDownEvent));
|
expect(events[0].runtimeType, equals(PointerDownEvent));
|
||||||
expect(events[1].runtimeType, equals(PointerCancelEvent));
|
expect(events[1].runtimeType, equals(PointerCancelEvent));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Can cancel pointers', () {
|
test('Can cancel pointers', () {
|
||||||
mojo_bindings.Encoder encoder = new mojo_bindings.Encoder();
|
ui.PointerDataPacket packet = new ui.PointerDataPacket(
|
||||||
|
pointers: <ui.PointerData>[
|
||||||
PointerPacket packet = new PointerPacket();
|
new ui.PointerData(change: ui.PointerChange.down),
|
||||||
packet.pointers = <Pointer>[new Pointer(), new Pointer()];
|
new ui.PointerData(change: ui.PointerChange.up),
|
||||||
packet.pointers[0].type = PointerType.down;
|
]
|
||||||
packet.pointers[0].kind = PointerKind.touch;
|
);
|
||||||
packet.pointers[1].type = PointerType.up;
|
|
||||||
packet.pointers[1].kind = PointerKind.touch;
|
|
||||||
packet.encode(encoder);
|
|
||||||
|
|
||||||
List<PointerEvent> events = <PointerEvent>[];
|
List<PointerEvent> events = <PointerEvent>[];
|
||||||
_binding.callback = (PointerEvent event) {
|
_binding.callback = (PointerEvent event) {
|
||||||
@ -143,7 +127,7 @@ void main() {
|
|||||||
_binding.cancelPointer(event.pointer);
|
_binding.cancelPointer(event.pointer);
|
||||||
};
|
};
|
||||||
|
|
||||||
ui.window.onPointerPacket(encoder.message.buffer);
|
ui.window.onPointerDataPacket(packet);
|
||||||
expect(events.length, 2);
|
expect(events.length, 2);
|
||||||
expect(events[0].runtimeType, equals(PointerDownEvent));
|
expect(events[0].runtimeType, equals(PointerDownEvent));
|
||||||
expect(events[1].runtimeType, equals(PointerCancelEvent));
|
expect(events[1].runtimeType, equals(PointerCancelEvent));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user