Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 99a81a8

Browse files
authored
Add support for double tap action from Apple Pencil 2 (#39267)
* working commit * some clean up * white space fixes * whitespace * remove unused import * Addressing PR comment, fix tests, update touch packet on Andriod * formatting n whitespace again * add new field to web ui pointer.dart * update test * whitespace * fix test * PR comments * fix test * whitespace * fix test by factoring out logic into helper function * whitespace * fix malformed string * pr comments * fix type issue * sigh whitespace * revert test changes :) * pr comments + separate out tests * extra space * change test name
1 parent 9ac09ce commit 99a81a8

12 files changed

+230
-37
lines changed

lib/ui/platform_dispatcher.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ class PlatformDispatcher {
371371
// * pointer_data.cc
372372
// * pointer.dart
373373
// * AndroidTouchProcessor.java
374-
static const int _kPointerDataFieldCount = 35;
374+
static const int _kPointerDataFieldCount = 36;
375375

376376
static PointerDataPacket _unpackPointerDataPacket(ByteData packet) {
377377
const int kStride = Int64List.bytesPerElement;
@@ -417,6 +417,7 @@ class PlatformDispatcher {
417417
panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
418418
scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
419419
rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian),
420+
preferredStylusAuxiliaryAction: PointerPreferredStylusAuxiliaryAction.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)],
420421
));
421422
assert(offset == (i + 1) * _kPointerDataFieldCount);
422423
}

lib/ui/pointer.dart

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,31 @@ enum PointerSignalKind {
133133
/// A pointer-generated scale event (e.g. trackpad pinch).
134134
scale,
135135

136+
/// A stylus generated action (e.g. double tap on Apple Pencil 2)
137+
stylusAuxiliaryAction,
138+
136139
/// An unknown pointer signal kind.
137140
unknown
138141
}
139142

143+
/// The preferred action for stylus action
144+
enum PointerPreferredStylusAuxiliaryAction {
145+
/// Ignore pointer input
146+
ignore,
147+
148+
/// Show colour palette if available
149+
showColorPalette,
150+
151+
/// Switch to eraser if available
152+
switchEraser,
153+
154+
/// Switch to previous tool
155+
switchPrevious,
156+
157+
/// unknown preferred action
158+
unknown,
159+
}
160+
140161
/// Information about the state of a pointer.
141162
class PointerData {
142163
/// Creates an object that represents the state of a pointer.
@@ -176,6 +197,7 @@ class PointerData {
176197
this.panDeltaY = 0.0,
177198
this.scale = 0.0,
178199
this.rotation = 0.0,
200+
this.preferredStylusAuxiliaryAction = PointerPreferredStylusAuxiliaryAction.ignore,
179201
});
180202

181203
/// Unique identifier that ties the [PointerEvent] to embedder event created it.
@@ -374,6 +396,11 @@ class PointerData {
374396
/// The current angle of the pan/zoom in radians, with 0.0 as the initial angle.
375397
final double rotation;
376398

399+
/// For events with signal kind of stylusAuxiliaryAction
400+
///
401+
/// The current preferred action for stylusAuxiliaryAction, with ignore as the default.
402+
final PointerPreferredStylusAuxiliaryAction preferredStylusAuxiliaryAction;
403+
377404
@override
378405
String toString() => 'PointerData(x: $physicalX, y: $physicalY)';
379406

@@ -413,7 +440,8 @@ class PointerData {
413440
'panDeltaX: $panDeltaX, '
414441
'panDeltaY: $panDeltaY, '
415442
'scale: $scale, '
416-
'rotation: $rotation'
443+
'rotation: $rotation, '
444+
'preferredStylusAuxiliaryAction: $preferredStylusAuxiliaryAction'
417445
')';
418446
}
419447
}

lib/ui/window/pointer_data.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace flutter {
1111

1212
// If this value changes, update the pointer data unpacking code in
1313
// platform_dispatcher.dart.
14-
static constexpr int kPointerDataFieldCount = 35;
14+
static constexpr int kPointerDataFieldCount = 36;
1515
static constexpr int kBytesPerField = sizeof(int64_t);
1616
// Must match the button constants in events.dart.
1717
enum PointerButtonMouse : int64_t {
@@ -63,6 +63,16 @@ struct alignas(8) PointerData {
6363
kScroll,
6464
kScrollInertiaCancel,
6565
kScale,
66+
kStylusAuxiliaryAction,
67+
};
68+
69+
// Must match the PreferredStylusAuxiliaryAction enum in pointer.dart.
70+
enum class PreferredStylusAuxiliaryAction : int64_t {
71+
kIgnore,
72+
kShowColorPalette,
73+
kSwitchEraser,
74+
kSwitchPrevious,
75+
kUnknown
6676
};
6777

6878
int64_t embedder_id;
@@ -100,6 +110,7 @@ struct alignas(8) PointerData {
100110
double pan_delta_y;
101111
double scale;
102112
double rotation;
113+
PreferredStylusAuxiliaryAction preferred_auxiliary_stylus_action;
103114

104115
void Clear();
105116
};

lib/ui/window/pointer_data_packet_converter.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ void PointerDataPacketConverter::ConvertPointerData(
293293
switch (pointer_data.signal_kind) {
294294
case PointerData::SignalKind::kScroll:
295295
case PointerData::SignalKind::kScrollInertiaCancel:
296+
case PointerData::SignalKind::kStylusAuxiliaryAction:
296297
case PointerData::SignalKind::kScale: {
297298
// Makes sure we have an existing pointer
298299
auto iter = states_.find(pointer_data.device);

lib/ui/window/pointer_data_packet_converter_unittests.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ void CreateSimulatedPointerData(PointerData& data, // NOLINT
4545
data.platformData = 0;
4646
data.scroll_delta_x = 0.0;
4747
data.scroll_delta_y = 0.0;
48+
data.preferred_auxiliary_stylus_action =
49+
PointerData::PreferredStylusAuxiliaryAction::kIgnore;
4850
}
4951

5052
void CreateSimulatedMousePointerData(PointerData& data, // NOLINT
@@ -84,6 +86,8 @@ void CreateSimulatedMousePointerData(PointerData& data, // NOLINT
8486
data.platformData = 0;
8587
data.scroll_delta_x = scroll_delta_x;
8688
data.scroll_delta_y = scroll_delta_y;
89+
data.preferred_auxiliary_stylus_action =
90+
PointerData::PreferredStylusAuxiliaryAction::kIgnore;
8791
}
8892

8993
void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT
@@ -129,6 +133,8 @@ void CreateSimulatedTrackpadGestureData(PointerData& data, // NOLINT
129133
data.pan_delta_y = 0.0;
130134
data.scale = scale;
131135
data.rotation = rotation;
136+
data.preferred_auxiliary_stylus_action =
137+
PointerData::PreferredStylusAuxiliaryAction::kIgnore;
132138
}
133139

134140
void UnpackPointerPacket(std::vector<PointerData>& output, // NOLINT

lib/web_ui/lib/pointer.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ enum PointerSignalKind {
3434
unknown
3535
}
3636

37+
/// The preferred action for stylus action
38+
enum PointerPreferredStylusAuxiliaryAction {
39+
/// Ignore pointer input
40+
ignore,
41+
42+
/// Show colour palette if available
43+
showColorPalette,
44+
45+
/// Switch to eraser if available
46+
switchEraser,
47+
48+
/// Switch to previous tool
49+
switchPrevious,
50+
51+
/// unknown preferred action
52+
unknown,
53+
}
54+
3755
class PointerData {
3856
const PointerData({
3957
this.embedderId = 0,
@@ -71,6 +89,7 @@ class PointerData {
7189
this.panDeltaY = 0.0,
7290
this.scale = 0.0,
7391
this.rotation = 0.0,
92+
this.preferredStylusAuxiliaryAction = PointerPreferredStylusAuxiliaryAction.ignore,
7493
});
7594
final int embedderId;
7695
final Duration timeStamp;
@@ -107,6 +126,7 @@ class PointerData {
107126
final double panDeltaY;
108127
final double scale;
109128
final double rotation;
129+
final PointerPreferredStylusAuxiliaryAction preferredStylusAuxiliaryAction;
110130

111131
@override
112132
String toString() => 'PointerData(x: $physicalX, y: $physicalY)';
@@ -145,7 +165,8 @@ class PointerData {
145165
'panDeltaX: $panDeltaX, '
146166
'panDeltaY: $panDeltaY, '
147167
'scale: $scale, '
148-
'rotation: $rotation'
168+
'rotation: $rotation, '
169+
'preferredStylusAuxiliaryAction: $preferredStylusAuxiliaryAction'
149170
')';
150171
}
151172
}

shell/common/input_events_unittests.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ void CreateSimulatedPointerData(PointerData& data,
176176
data.platformData = 0;
177177
data.scroll_delta_x = 0.0;
178178
data.scroll_delta_y = 0.0;
179+
data.preferred_auxiliary_stylus_action =
180+
PointerData::PreferredStylusAuxiliaryAction::kIgnore;
179181
}
180182

181183
TEST_F(ShellTest, MissAtMostOneFrameForIrregularInputEvents) {

shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,36 @@ public class AndroidTouchProcessor {
6666
PointerSignalKind.SCROLL,
6767
PointerSignalKind.SCROLL_INERTIA_CANCEL,
6868
PointerSignalKind.SCALE,
69+
PointerSignalKind.STYLUS_AUXILIARY_ACTION,
6970
PointerSignalKind.UNKNOWN
7071
})
7172
public @interface PointerSignalKind {
7273
int NONE = 0;
7374
int SCROLL = 1;
7475
int SCROLL_INERTIA_CANCEL = 2;
7576
int SCALE = 3;
77+
int STYLUS_AUXILIARY_ACTION = 4;
78+
int UNKNOWN = 5;
79+
}
80+
81+
// Must match the PointerPreferredStylusAuxiliaryAction enum in pointer.dart.
82+
@IntDef({
83+
PointerPreferredStylusAuxiliaryAction.IGNORE,
84+
PointerPreferredStylusAuxiliaryAction.SHOW_COLOR_PALETTE,
85+
PointerPreferredStylusAuxiliaryAction.SWITCH_ERASER,
86+
PointerPreferredStylusAuxiliaryAction.SWITCH_PREVIOUS,
87+
PointerPreferredStylusAuxiliaryAction.UNKNOWN
88+
})
89+
public @interface PointerPreferredStylusAuxiliaryAction {
90+
int IGNORE = 0;
91+
int SHOW_COLOR_PALETTE = 1;
92+
int SWITCH_ERASER = 2;
93+
int SWITCH_PREVIOUS = 3;
7694
int UNKNOWN = 4;
7795
}
7896

7997
// Must match the unpacking code in hooks.dart.
80-
private static final int POINTER_DATA_FIELD_COUNT = 35;
98+
private static final int POINTER_DATA_FIELD_COUNT = 36;
8199
@VisibleForTesting static final int BYTES_PER_FIELD = 8;
82100

83101
// This value must match the value in framework's platform_view.dart.
@@ -355,6 +373,8 @@ private void addPointerForIndex(
355373
packet.putDouble(1.0); // scale
356374
packet.putDouble(0.0); // rotation
357375

376+
packet.putLong(PointerPreferredStylusAuxiliaryAction.IGNORE); // preferred stylus action
377+
358378
if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) {
359379
ongoingPans.remove(event.getPointerId(pointerIndex));
360380
}

shell/platform/darwin/ios/framework/Source/FlutterViewController.mm

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
// This is left a FlutterBinaryMessenger privately for now to give people a chance to notice the
5757
// change. Unfortunately unless you have Werror turned on, incompatible pointers as arguments are
5858
// just a warning.
59-
@interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegate>
59+
@interface FlutterViewController () <FlutterBinaryMessenger,
60+
UIScrollViewDelegate,
61+
UIPencilInteractionDelegate>
6062
@property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI;
6163
@property(nonatomic, assign) BOOL isHomeIndicatorHidden;
6264
@property(nonatomic, assign) BOOL isPresentingViewControllerAnimating;
@@ -97,7 +99,7 @@ @interface FlutterViewController () <FlutterBinaryMessenger, UIScrollViewDelegat
9799
// Trackpad rotating
98100
@property(nonatomic, retain)
99101
UIRotationGestureRecognizer* rotationGestureRecognizer API_AVAILABLE(ios(13.4));
100-
102+
@property(nonatomic, retain) UIPencilInteraction* pencilInteraction API_AVAILABLE(ios(13.4));
101103
/**
102104
* Creates and registers plugins used by this view controller.
103105
*/
@@ -722,6 +724,10 @@ - (void)viewDidLoad {
722724
[self createTouchRateCorrectionVSyncClientIfNeeded];
723725

724726
if (@available(iOS 13.4, *)) {
727+
_pencilInteraction = [[UIPencilInteraction alloc] init];
728+
_pencilInteraction.delegate = self;
729+
[_flutterView addInteraction:_pencilInteraction];
730+
725731
_hoverGestureRecognizer =
726732
[[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hoverEvent:)];
727733
_hoverGestureRecognizer.delegate = self;
@@ -895,6 +901,8 @@ - (void)dealloc {
895901
[_pinchGestureRecognizer release];
896902
_rotationGestureRecognizer.delegate = nil;
897903
[_rotationGestureRecognizer release];
904+
_pencilInteraction.delegate = nil;
905+
[_pencilInteraction release];
898906
[super dealloc];
899907
}
900908

@@ -969,7 +977,7 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state {
969977
case UITouchTypeDirect:
970978
case UITouchTypeIndirect:
971979
return flutter::PointerData::DeviceKind::kTouch;
972-
case UITouchTypeStylus:
980+
case UITouchTypePencil:
973981
return flutter::PointerData::DeviceKind::kStylus;
974982
case UITouchTypeIndirectPointer:
975983
return flutter::PointerData::DeviceKind::kMouse;
@@ -1224,6 +1232,50 @@ - (void)invalidateTouchRateCorrectionVSyncClient {
12241232
_touchRateCorrectionVSyncClient = nil;
12251233
}
12261234

1235+
#pragma mark - Stylus Events
1236+
1237+
- (void)pencilInteractionDidTap:(UIPencilInteraction*)interaction API_AVAILABLE(ios(13.4)) {
1238+
flutter::PointerData pointer_data = [self createAuxillaryStylusActionData];
1239+
1240+
auto packet = std::make_unique<flutter::PointerDataPacket>(1);
1241+
packet->SetPointerData(/*index=*/0, pointer_data);
1242+
[_engine.get() dispatchPointerDataPacket:std::move(packet)];
1243+
}
1244+
1245+
- (flutter::PointerData)createAuxillaryStylusActionData API_AVAILABLE(ios(13.4)) {
1246+
flutter::PointerData pointer_data;
1247+
pointer_data.Clear();
1248+
1249+
switch (UIPencilInteraction.preferredTapAction) {
1250+
case UIPencilPreferredActionIgnore:
1251+
pointer_data.preferred_auxiliary_stylus_action =
1252+
flutter::PointerData::PreferredStylusAuxiliaryAction::kIgnore;
1253+
break;
1254+
case UIPencilPreferredActionShowColorPalette:
1255+
pointer_data.preferred_auxiliary_stylus_action =
1256+
flutter::PointerData::PreferredStylusAuxiliaryAction::kShowColorPalette;
1257+
break;
1258+
case UIPencilPreferredActionSwitchEraser:
1259+
pointer_data.preferred_auxiliary_stylus_action =
1260+
flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchEraser;
1261+
break;
1262+
case UIPencilPreferredActionSwitchPrevious:
1263+
pointer_data.preferred_auxiliary_stylus_action =
1264+
flutter::PointerData::PreferredStylusAuxiliaryAction::kSwitchPrevious;
1265+
break;
1266+
default:
1267+
pointer_data.preferred_auxiliary_stylus_action =
1268+
flutter::PointerData::PreferredStylusAuxiliaryAction::kUnknown;
1269+
break;
1270+
}
1271+
1272+
pointer_data.time_stamp = [[NSProcessInfo processInfo] systemUptime] * kMicrosecondsPerSecond;
1273+
pointer_data.kind = flutter::PointerData::DeviceKind::kStylus;
1274+
pointer_data.signal_kind = flutter::PointerData::SignalKind::kStylusAuxiliaryAction;
1275+
1276+
return pointer_data;
1277+
}
1278+
12271279
#pragma mark - Handle view resizing
12281280

12291281
- (void)updateViewportMetrics {

0 commit comments

Comments
 (0)