Skip to content

Commit 694c31c

Browse files
committed
Implement trackpad gestures in engine
1 parent 1e2ba8f commit 694c31c

28 files changed

+1603
-113
lines changed

lib/ui/platform_dispatcher.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ class PlatformDispatcher {
341341
// * pointer_data.cc
342342
// * pointer.dart
343343
// * AndroidTouchProcessor.java
344-
static const int _kPointerDataFieldCount = 29;
344+
static const int _kPointerDataFieldCount = 35;
345345

346346
static PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
347347
const int kStride = Int64List.bytesPerElement;
@@ -381,6 +381,12 @@ class PlatformDispatcher {
381381
platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian),
382382
scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
383383
scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
384+
panX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
385+
panY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
386+
panDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
387+
panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
388+
scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
389+
angle: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
384390
));
385391
assert(offset == (i + 1) * _kPointerDataFieldCount);
386392
}

lib/ui/pointer.dart

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ enum PointerChange {
3636

3737
/// The pointer has stopped making contact with the device.
3838
up,
39+
40+
/// An offscreen interaction gesture has started (such as trackpad gesture)
41+
flowStart,
42+
43+
/// An offscreen interaction has changed
44+
flowUpdate,
45+
46+
/// An offscreen interaction has ended
47+
flowEnd,
3948
}
4049

4150
/// The kind of pointer device.
@@ -101,6 +110,12 @@ class PointerData {
101110
this.platformData = 0,
102111
this.scrollDeltaX = 0.0,
103112
this.scrollDeltaY = 0.0,
113+
this.panX = 0.0,
114+
this.panY = 0.0,
115+
this.panDeltaX = 0.0,
116+
this.panDeltaY = 0.0,
117+
this.scale = 0.0,
118+
this.angle = 0.0,
104119
});
105120

106121
/// Unique identifier that ties the [PointerEvent] to embedder event created it.
@@ -265,6 +280,36 @@ class PointerData {
265280
/// The amount to scroll in the y direction, in physical pixels.
266281
final double scrollDeltaY;
267282

283+
/// For events with change of PointerChange.flowUpdate:
284+
///
285+
/// The current panning magnitude of the gesture in the x direction, in physical pixels.
286+
final double panX;
287+
288+
/// For events with change of PointerChange.flowUpdate:
289+
///
290+
/// The current panning magnitude of the gesture in the x direction, in physical pixels.
291+
final double panY;
292+
293+
/// For events with change of PointerChange.flowUpdate:
294+
///
295+
/// The difference in panning of the gesture in the x direction since the latest flowUpdate event, in physical pixels.
296+
final double panDeltaX;
297+
298+
/// For events with change of PointerChange.flowUpdate:
299+
///
300+
/// The difference in panning of the gesture in the y direction since the last flowUpdate event, in physical pixels.
301+
final double panDeltaY;
302+
303+
/// For events with change of PointerChange.flowUpdate:
304+
///
305+
/// The current scale of the gesture (unitless), with 1.0 as the initial scale.
306+
final double scale;
307+
308+
/// For events with change of PointerChange.flowUpdate:
309+
///
310+
/// The current angle of the gesture in radians, with 0.0 as the initial angle.
311+
final double angle;
312+
268313
@override
269314
String toString() => 'PointerData(x: $physicalX, y: $physicalY)';
270315

@@ -298,7 +343,13 @@ class PointerData {
298343
'tilt: $tilt, '
299344
'platformData: $platformData, '
300345
'scrollDeltaX: $scrollDeltaX, '
301-
'scrollDeltaY: $scrollDeltaY'
346+
'scrollDeltaY: $scrollDeltaY, '
347+
'panX: $panX, '
348+
'panY: $panY, '
349+
'panDeltaX: $panDeltaX, '
350+
'panDeltaY: $panDeltaY, '
351+
'scale: $scale, '
352+
'angle: $angle'
302353
')';
303354
}
304355
}

lib/ui/window/pointer_data.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace flutter {
1111

1212
// If this value changes, update the pointer data unpacking code in hooks.dart.
13-
static constexpr int kPointerDataFieldCount = 29;
13+
static constexpr int kPointerDataFieldCount = 35;
1414
static constexpr int kBytesPerField = sizeof(int64_t);
1515
// Must match the button constants in events.dart.
1616
enum PointerButtonMouse : int64_t {
@@ -42,6 +42,9 @@ struct alignas(8) PointerData {
4242
kDown,
4343
kMove,
4444
kUp,
45+
kFlowStart,
46+
kFlowUpdate,
47+
kFlowEnd,
4548
};
4649

4750
// Must match the PointerDeviceKind enum in pointer.dart.
@@ -53,10 +56,7 @@ struct alignas(8) PointerData {
5356
};
5457

5558
// Must match the PointerSignalKind enum in pointer.dart.
56-
enum class SignalKind : int64_t {
57-
kNone,
58-
kScroll,
59-
};
59+
enum class SignalKind : int64_t { kNone, kScroll };
6060

6161
int64_t embedder_id;
6262
int64_t time_stamp;
@@ -87,6 +87,12 @@ struct alignas(8) PointerData {
8787
int64_t platformData;
8888
double scroll_delta_x;
8989
double scroll_delta_y;
90+
double pan_x;
91+
double pan_y;
92+
double pan_delta_x;
93+
double pan_delta_y;
94+
double scale;
95+
double angle;
9096

9197
void Clear();
9298
};

lib/ui/window/pointer_data_packet_converter.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "flutter/lib/ui/window/pointer_data_packet_converter.h"
66

7+
#include <cmath>
78
#include <cstring>
89

910
#include "flutter/fml/logging.h"
@@ -207,6 +208,88 @@ void PointerDataPacketConverter::ConvertPointerData(
207208
converted_pointers.push_back(pointer_data);
208209
break;
209210
}
211+
case PointerData::Change::kFlowStart: {
212+
// Makes sure we have an existing pointer
213+
auto iter = states_.find(pointer_data.device);
214+
PointerState state;
215+
if (iter == states_.end()) {
216+
// Synthesizes add event if the pointer is not previously added.
217+
PointerData synthesized_add_event = pointer_data;
218+
synthesized_add_event.change = PointerData::Change::kAdd;
219+
synthesized_add_event.synthesized = 1;
220+
synthesized_add_event.buttons = 0;
221+
state = EnsurePointerState(synthesized_add_event);
222+
converted_pointers.push_back(synthesized_add_event);
223+
} else {
224+
state = iter->second;
225+
}
226+
FML_DCHECK(!state.is_down);
227+
FML_DCHECK(!state.is_flow_active);
228+
if (LocationNeedsUpdate(pointer_data, state)) {
229+
// Synthesizes a hover event if the location does not match.
230+
PointerData synthesized_hover_event = pointer_data;
231+
synthesized_hover_event.change = PointerData::Change::kHover;
232+
synthesized_hover_event.synthesized = 1;
233+
synthesized_hover_event.buttons = 0;
234+
235+
UpdateDeltaAndState(synthesized_hover_event, state);
236+
converted_pointers.push_back(synthesized_hover_event);
237+
}
238+
239+
UpdatePointerIdentifier(pointer_data, state, true);
240+
state.is_flow_active = true;
241+
state.pan_x = 0;
242+
state.pan_y = 0;
243+
state.scale = 1;
244+
state.angle = 0;
245+
states_[pointer_data.device] = state;
246+
converted_pointers.push_back(pointer_data);
247+
break;
248+
}
249+
case PointerData::Change::kFlowUpdate: {
250+
// Makes sure we have an existing pointer in flow_active state
251+
auto iter = states_.find(pointer_data.device);
252+
FML_DCHECK(iter != states_.end());
253+
PointerState state = iter->second;
254+
FML_DCHECK(!state.is_down);
255+
FML_DCHECK(state.is_flow_active);
256+
257+
UpdatePointerIdentifier(pointer_data, state, false);
258+
UpdateDeltaAndState(pointer_data, state);
259+
260+
converted_pointers.push_back(pointer_data);
261+
break;
262+
}
263+
case PointerData::Change::kFlowEnd: {
264+
// Makes sure we have an existing pointer in flow_active state
265+
auto iter = states_.find(pointer_data.device);
266+
FML_DCHECK(iter != states_.end());
267+
PointerState state = iter->second;
268+
FML_DCHECK(state.is_flow_active);
269+
270+
UpdatePointerIdentifier(pointer_data, state, false);
271+
272+
if (LocationNeedsUpdate(pointer_data, state)) {
273+
// Synthesizes an update event if the location does not match.
274+
PointerData synthesized_move_event = pointer_data;
275+
synthesized_move_event.change = PointerData::Change::kFlowUpdate;
276+
synthesized_move_event.pan_x = state.pan_x;
277+
synthesized_move_event.pan_y = state.pan_y;
278+
synthesized_move_event.pan_delta_x = 0;
279+
synthesized_move_event.pan_delta_y = 0;
280+
synthesized_move_event.scale = state.scale;
281+
synthesized_move_event.angle = state.angle;
282+
synthesized_move_event.synthesized = 1;
283+
284+
UpdateDeltaAndState(synthesized_move_event, state);
285+
converted_pointers.push_back(synthesized_move_event);
286+
}
287+
288+
state.is_flow_active = false;
289+
states_[pointer_data.device] = state;
290+
converted_pointers.push_back(pointer_data);
291+
break;
292+
}
210293
default: {
211294
converted_pointers.push_back(pointer_data);
212295
break;
@@ -261,8 +344,11 @@ PointerState PointerDataPacketConverter::EnsurePointerState(
261344
PointerState state;
262345
state.pointer_identifier = 0;
263346
state.is_down = false;
347+
state.is_flow_active = false;
264348
state.physical_x = pointer_data.physical_x;
265349
state.physical_y = pointer_data.physical_y;
350+
state.pan_x = pointer_data.pan_x;
351+
state.pan_y = pointer_data.pan_y;
266352
states_[pointer_data.device] = state;
267353
return state;
268354
}
@@ -271,8 +357,14 @@ void PointerDataPacketConverter::UpdateDeltaAndState(PointerData& pointer_data,
271357
PointerState& state) {
272358
pointer_data.physical_delta_x = pointer_data.physical_x - state.physical_x;
273359
pointer_data.physical_delta_y = pointer_data.physical_y - state.physical_y;
360+
pointer_data.pan_delta_x = pointer_data.pan_x - state.pan_x;
361+
pointer_data.pan_delta_y = pointer_data.pan_y - state.pan_y;
274362
state.physical_x = pointer_data.physical_x;
275363
state.physical_y = pointer_data.physical_y;
364+
state.pan_x = pointer_data.pan_x;
365+
state.pan_y = pointer_data.pan_y;
366+
state.scale = pointer_data.scale;
367+
state.angle = pointer_data.angle;
276368
states_[pointer_data.device] = state;
277369
}
278370

lib/ui/window/pointer_data_packet_converter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ namespace flutter {
3030
struct PointerState {
3131
int64_t pointer_identifier;
3232
bool is_down;
33+
bool is_flow_active;
3334
double physical_x;
3435
double physical_y;
36+
double pan_x;
37+
double pan_y;
38+
double scale;
39+
double angle;
3540
int64_t buttons;
3641
};
3742

lib/ui/window/pointer_data_packet_converter_unittests.cc

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,51 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT
8686
data.scroll_delta_y = scroll_delta_y;
8787
}
8888

89+
void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT
90+
PointerData::Change change,
91+
int64_t device,
92+
double dx,
93+
double dy,
94+
double pan_x,
95+
double pan_y,
96+
double scale,
97+
double angle) {
98+
data.time_stamp = 0;
99+
data.change = change;
100+
data.kind = PointerData::DeviceKind::kMouse;
101+
data.signal_kind = PointerData::SignalKind::kNone;
102+
data.device = device;
103+
data.pointer_identifier = 0;
104+
data.physical_x = dx;
105+
data.physical_y = dy;
106+
data.physical_delta_x = 0.0;
107+
data.physical_delta_y = 0.0;
108+
data.buttons = 0;
109+
data.obscured = 0;
110+
data.synthesized = 0;
111+
data.pressure = 0.0;
112+
data.pressure_min = 0.0;
113+
data.pressure_max = 0.0;
114+
data.distance = 0.0;
115+
data.distance_max = 0.0;
116+
data.size = 0.0;
117+
data.radius_major = 0.0;
118+
data.radius_minor = 0.0;
119+
data.radius_min = 0.0;
120+
data.radius_max = 0.0;
121+
data.orientation = 0.0;
122+
data.tilt = 0.0;
123+
data.platformData = 0;
124+
data.scroll_delta_x = 0.0;
125+
data.scroll_delta_y = 0.0;
126+
data.pan_x = pan_x;
127+
data.pan_y = pan_y;
128+
data.pan_delta_x = 0.0;
129+
data.pan_delta_y = 0.0;
130+
data.scale = scale;
131+
data.angle = angle;
132+
}
133+
89134
void UnpackPointerPacket(std::vector<PointerData>& output, // NOLINT
90135
std::unique_ptr<PointerDataPacket> packet) {
91136
size_t kBytesPerPointerData = kPointerDataFieldCount * kBytesPerField;
@@ -599,5 +644,56 @@ TEST(PointerDataPacketConverterTest, CanConvetScroll) {
599644
ASSERT_EQ(result[6].scroll_delta_y, 0.0);
600645
}
601646

647+
TEST(PointerDataPacketConverterTest, CanConvertTrackpadGesture) {
648+
PointerDataPacketConverter converter;
649+
auto packet = std::make_unique<PointerDataPacket>(3);
650+
PointerData data;
651+
CreateSimulatedTrackpadGestureData(data, PointerData::Change::kFlowStart, 0,
652+
0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
653+
packet->SetPointerData(0, data);
654+
CreateSimulatedTrackpadGestureData(data, PointerData::Change::kFlowUpdate, 0,
655+
0.0, 0.0, 3.0, 4.0, 1.0, 0.0);
656+
packet->SetPointerData(1, data);
657+
CreateSimulatedTrackpadGestureData(data, PointerData::Change::kFlowEnd, 0,
658+
0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
659+
packet->SetPointerData(2, data);
660+
auto converted_packet = converter.Convert(std::move(packet));
661+
662+
std::vector<PointerData> result;
663+
UnpackPointerPacket(result, std::move(converted_packet));
664+
665+
ASSERT_EQ(result.size(), (size_t)4);
666+
ASSERT_EQ(result[0].change, PointerData::Change::kAdd);
667+
ASSERT_EQ(result[0].device, 0);
668+
ASSERT_EQ(result[0].synthesized, 1);
669+
670+
ASSERT_EQ(result[1].change, PointerData::Change::kFlowStart);
671+
ASSERT_EQ(result[1].signal_kind, PointerData::SignalKind::kNone);
672+
ASSERT_EQ(result[1].device, 0);
673+
ASSERT_EQ(result[1].physical_x, 0.0);
674+
ASSERT_EQ(result[1].physical_y, 0.0);
675+
ASSERT_EQ(result[1].synthesized, 0);
676+
677+
ASSERT_EQ(result[2].change, PointerData::Change::kFlowUpdate);
678+
ASSERT_EQ(result[2].signal_kind, PointerData::SignalKind::kNone);
679+
ASSERT_EQ(result[2].device, 0);
680+
ASSERT_EQ(result[2].physical_x, 0.0);
681+
ASSERT_EQ(result[2].physical_y, 0.0);
682+
ASSERT_EQ(result[2].pan_x, 3.0);
683+
ASSERT_EQ(result[2].pan_y, 4.0);
684+
ASSERT_EQ(result[2].pan_delta_x, 3.0);
685+
ASSERT_EQ(result[2].pan_delta_y, 4.0);
686+
ASSERT_EQ(result[2].scale, 1.0);
687+
ASSERT_EQ(result[2].angle, 0.0);
688+
ASSERT_EQ(result[2].synthesized, 0);
689+
690+
ASSERT_EQ(result[3].change, PointerData::Change::kFlowEnd);
691+
ASSERT_EQ(result[3].signal_kind, PointerData::SignalKind::kNone);
692+
ASSERT_EQ(result[3].device, 0);
693+
ASSERT_EQ(result[3].physical_x, 0.0);
694+
ASSERT_EQ(result[3].physical_y, 0.0);
695+
ASSERT_EQ(result[3].synthesized, 0);
696+
}
697+
602698
} // namespace testing
603699
} // namespace flutter

0 commit comments

Comments
 (0)