Skip to content

Commit 980c52d

Browse files
Andrei Shikovfacebook-github-bot
Andrei Shikov
authored andcommitted
Disable view flattening when the view has event handlers on Android
Summary: The views with touch event props are currently flattened by Fabric core, as we don't take event listeners into account when calculating whether the view should be flattened. This results in a confusing situation when components with touch event listeners (e.g. `<View onTouchStart={() => {}} /> `) or ones using `PanResponder` are either ignored (iOS) or cause a crash (Android). This change passes touch event props to C++ layer and uses them to calculate whether the view node should be flattened or not. It also refactors events to be kept as a singular bitset with 32 bit (~`uint32_t`). Changelog: [Changed][General] Avoid flattening nodes with event props Reviewed By: sammy-SC Differential Revision: D34005536 fbshipit-source-id: 96255b389a7bfff4aa208a96fd0c173d9edf1512
1 parent 9ed2df6 commit 980c52d

File tree

10 files changed

+352
-43
lines changed

10 files changed

+352
-43
lines changed

Libraries/NativeComponent/PlatformBaseViewConfig.js

+49-6
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
3838
registrationName: 'onAccessibilityAction',
3939
},
4040
topPointerEnter: {
41-
registrationName: 'pointerenter',
41+
registrationName: 'onPointerEnter',
4242
},
4343
topPointerLeave: {
44-
registrationName: 'pointerleave',
44+
registrationName: 'onPointerLeave',
4545
},
4646
topPointerMove: {
47-
registrationName: 'pointermove',
47+
registrationName: 'onPointerMove',
4848
},
4949
onGestureHandlerEvent: DynamicallyInjectedByGestureHandler({
5050
registrationName: 'onGestureHandlerEvent',
@@ -219,9 +219,31 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
219219
position: true,
220220
onLayout: true,
221221

222-
pointerenter: true,
223-
pointerleave: true,
224-
pointermove: true,
222+
// Pointer events
223+
onPointerEnter: true,
224+
onPointerLeave: true,
225+
onPointerMove: true,
226+
227+
// PanResponder handlers
228+
onMoveShouldSetResponder: true,
229+
onMoveShouldSetResponderCapture: true,
230+
onStartShouldSetResponder: true,
231+
onStartShouldSetResponderCapture: true,
232+
onResponderGrant: true,
233+
onResponderReject: true,
234+
onResponderStart: true,
235+
onResponderEnd: true,
236+
onResponderRelease: true,
237+
onResponderMove: true,
238+
onResponderTerminate: true,
239+
onResponderTerminationRequest: true,
240+
onShouldBlockNativeResponder: true,
241+
242+
// Touch events
243+
onTouchStart: true,
244+
onTouchMove: true,
245+
onTouchEnd: true,
246+
onTouchCancel: true,
225247

226248
style: ReactNativeStyleAttributes,
227249
},
@@ -456,6 +478,27 @@ const PlatformBaseViewConfig: PartialViewConfigWithoutName =
456478
onAccessibilityAction: true,
457479
onAccessibilityEscape: true,
458480
onAccessibilityTap: true,
481+
482+
// PanResponder handlers
483+
onMoveShouldSetResponder: true,
484+
onMoveShouldSetResponderCapture: true,
485+
onStartShouldSetResponder: true,
486+
onStartShouldSetResponderCapture: true,
487+
onResponderGrant: true,
488+
onResponderReject: true,
489+
onResponderStart: true,
490+
onResponderEnd: true,
491+
onResponderRelease: true,
492+
onResponderMove: true,
493+
onResponderTerminate: true,
494+
onResponderTerminationRequest: true,
495+
onShouldBlockNativeResponder: true,
496+
497+
// Touch events
498+
onTouchStart: true,
499+
onTouchMove: true,
500+
onTouchEnd: true,
501+
onTouchCancel: true,
459502
}),
460503
},
461504
};

React/Views/RCTViewManager.m

+25
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,29 @@ - (RCTShadowView *)shadowView
418418

419419
RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection)
420420

421+
// The events below define the properties that are not used by native directly, but required in the view config for new
422+
// renderer to function.
423+
// They can be deleted after Static View Configs are rolled out.
424+
425+
// PanResponder handlers
426+
RCT_CUSTOM_VIEW_PROPERTY(onMoveShouldSetResponder, BOOL, RCTView) {}
427+
RCT_CUSTOM_VIEW_PROPERTY(onMoveShouldSetResponderCapture, BOOL, RCTView) {}
428+
RCT_CUSTOM_VIEW_PROPERTY(onStartShouldSetResponder, BOOL, RCTView) {}
429+
RCT_CUSTOM_VIEW_PROPERTY(onStartShouldSetResponderCapture, BOOL, RCTView) {}
430+
RCT_CUSTOM_VIEW_PROPERTY(onResponderGrant, BOOL, RCTView) {}
431+
RCT_CUSTOM_VIEW_PROPERTY(onResponderReject, BOOL, RCTView) {}
432+
RCT_CUSTOM_VIEW_PROPERTY(onResponderStart, BOOL, RCTView) {}
433+
RCT_CUSTOM_VIEW_PROPERTY(onResponderEnd, BOOL, RCTView) {}
434+
RCT_CUSTOM_VIEW_PROPERTY(onResponderRelease, BOOL, RCTView) {}
435+
RCT_CUSTOM_VIEW_PROPERTY(onResponderMove, BOOL, RCTView) {}
436+
RCT_CUSTOM_VIEW_PROPERTY(onResponderTerminate, BOOL, RCTView) {}
437+
RCT_CUSTOM_VIEW_PROPERTY(onResponderTerminationRequest, BOOL, RCTView) {}
438+
RCT_CUSTOM_VIEW_PROPERTY(onShouldBlockNativeResponder, BOOL, RCTView) {}
439+
440+
// Touch events
441+
RCT_CUSTOM_VIEW_PROPERTY(onTouchStart, BOOL, RCTView) {}
442+
RCT_CUSTOM_VIEW_PROPERTY(onTouchMove, BOOL, RCTView) {}
443+
RCT_CUSTOM_VIEW_PROPERTY(onTouchEnd, BOOL, RCTView) {}
444+
RCT_CUSTOM_VIEW_PROPERTY(onTouchCancel, BOOL, RCTView) {}
445+
421446
@end

ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java

+91-6
Original file line numberDiff line numberDiff line change
@@ -486,18 +486,103 @@ private void logUnsupportedPropertyWarning(String propName) {
486486
FLog.w(ReactConstants.TAG, "%s doesn't support property '%s'", getName(), propName);
487487
}
488488

489-
@ReactProp(name = "pointerenter")
490-
public void setPointerEnter(@NonNull T view, @Nullable boolean value) {
489+
@ReactProp(name = "onPointerEnter")
490+
public void setPointerEnter(@NonNull T view, boolean value) {
491491
view.setTag(R.id.pointer_enter, value);
492492
}
493493

494-
@ReactProp(name = "pointerleave")
495-
public void setPointerLeave(@NonNull T view, @Nullable boolean value) {
494+
@ReactProp(name = "onPointerLeave")
495+
public void setPointerLeave(@NonNull T view, boolean value) {
496496
view.setTag(R.id.pointer_leave, value);
497497
}
498498

499-
@ReactProp(name = "pointermove")
500-
public void setPointerMove(@NonNull T view, @Nullable boolean value) {
499+
@ReactProp(name = "onPointerMove")
500+
public void setPointerMove(@NonNull T view, boolean value) {
501501
view.setTag(R.id.pointer_move, value);
502502
}
503+
504+
@ReactProp(name = "onMoveShouldSetResponder")
505+
public void setMoveShouldSetResponder(@NonNull T view, boolean value) {
506+
// no-op, handled by JSResponder
507+
}
508+
509+
@ReactProp(name = "onMoveShouldSetResponderCapture")
510+
public void setMoveShouldSetResponderCapture(@NonNull T view, boolean value) {
511+
// no-op, handled by JSResponder
512+
}
513+
514+
@ReactProp(name = "onStartShouldSetResponder")
515+
public void setStartShouldSetResponder(@NonNull T view, boolean value) {
516+
// no-op, handled by JSResponder
517+
}
518+
519+
@ReactProp(name = "onStartShouldSetResponderCapture")
520+
public void setStartShouldSetResponderCapture(@NonNull T view, boolean value) {
521+
// no-op, handled by JSResponder
522+
}
523+
524+
@ReactProp(name = "onResponderGrant")
525+
public void setResponderGrant(@NonNull T view, boolean value) {
526+
// no-op, handled by JSResponder
527+
}
528+
529+
@ReactProp(name = "onResponderReject")
530+
public void setResponderReject(@NonNull T view, boolean value) {
531+
// no-op, handled by JSResponder
532+
}
533+
534+
@ReactProp(name = "onResponderStart")
535+
public void setResponderStart(@NonNull T view, boolean value) {
536+
// no-op, handled by JSResponder
537+
}
538+
539+
@ReactProp(name = "onResponderEnd")
540+
public void setResponderEnd(@NonNull T view, boolean value) {
541+
// no-op, handled by JSResponder
542+
}
543+
544+
@ReactProp(name = "onResponderRelease")
545+
public void setResponderRelease(@NonNull T view, boolean value) {
546+
// no-op, handled by JSResponder
547+
}
548+
549+
@ReactProp(name = "onResponderMove")
550+
public void setResponderMove(@NonNull T view, boolean value) {
551+
// no-op, handled by JSResponder
552+
}
553+
554+
@ReactProp(name = "onResponderTerminate")
555+
public void setResponderTerminate(@NonNull T view, boolean value) {
556+
// no-op, handled by JSResponder
557+
}
558+
559+
@ReactProp(name = "onResponderTerminationRequest")
560+
public void setResponderTerminationRequest(@NonNull T view, boolean value) {
561+
// no-op, handled by JSResponder
562+
}
563+
564+
@ReactProp(name = "onShouldBlockNativeResponder")
565+
public void setShouldBlockNativeResponder(@NonNull T view, boolean value) {
566+
// no-op, handled by JSResponder
567+
}
568+
569+
@ReactProp(name = "onTouchStart")
570+
public void setTouchStart(@NonNull T view, boolean value) {
571+
// no-op, handled by JSResponder
572+
}
573+
574+
@ReactProp(name = "onTouchMove")
575+
public void setTouchMove(@NonNull T view, boolean value) {
576+
// no-op, handled by JSResponder
577+
}
578+
579+
@ReactProp(name = "onTouchEnd")
580+
public void setTouchEnd(@NonNull T view, boolean value) {
581+
// no-op, handled by JSResponder
582+
}
583+
584+
@ReactProp(name = "onTouchCancel")
585+
public void setTouchCancel(@NonNull T view, boolean value) {
586+
// no-op, handled by JSResponder
587+
}
503588
}

ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -808,19 +808,19 @@ public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
808808
super.setShouldNotifyOnLayout(shouldNotifyOnLayout);
809809
}
810810

811-
@ReactProp(name = "pointerenter")
811+
@ReactProp(name = "onPointerEnter")
812812
public void setShouldNotifyPointerEnter(boolean value) {
813813
// This method exists to inject Native View configs in RN Android VR
814814
// DO NOTHING
815815
}
816816

817-
@ReactProp(name = "pointerleave")
817+
@ReactProp(name = "onPointerLeave")
818818
public void setShouldNotifyPointerLeave(boolean value) {
819819
// This method exists to inject Native View configs in RN Android VR
820820
// DO NOTHING
821821
}
822822

823-
@ReactProp(name = "pointermove")
823+
@ReactProp(name = "onPointerMove")
824824
public void setShouldNotifyPointerMove(boolean value) {
825825
// This method exists to inject Native View configs in RN Android VR
826826
// DO NOTHING

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@
5959
return MapBuilder.builder()
6060
.put("topContentSizeChange", MapBuilder.of(rn, "onContentSizeChange"))
6161
.put("topLayout", MapBuilder.of(rn, "onLayout"))
62-
.put("topPointerEnter", MapBuilder.of(rn, "pointerenter"))
63-
.put("topPointerLeave", MapBuilder.of(rn, "pointerleave"))
64-
.put("topPointerMove", MapBuilder.of(rn, "pointermove"))
62+
.put("topPointerEnter", MapBuilder.of(rn, "onPointerEnter"))
63+
.put("topPointerLeave", MapBuilder.of(rn, "onPointerLeave"))
64+
.put("topPointerMove", MapBuilder.of(rn, "onPointerMove"))
6565
.put("topLoadingError", MapBuilder.of(rn, "onLoadingError"))
6666
.put("topLoadingFinish", MapBuilder.of(rn, "onLoadingFinish"))
6767
.put("topLoadingStart", MapBuilder.of(rn, "onLoadingStart"))

ReactCommon/react/renderer/components/view/ViewProps.cpp

+1-18
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,7 @@ ViewProps::ViewProps(
125125
"onLayout",
126126
sourceProps.onLayout,
127127
{})),
128-
pointerEnter(convertRawProp(
129-
context,
130-
rawProps,
131-
"pointerenter",
132-
sourceProps.pointerEnter,
133-
{})),
134-
pointerLeave(convertRawProp(
135-
context,
136-
rawProps,
137-
"pointerleave",
138-
sourceProps.pointerLeave,
139-
{})),
140-
pointerMove(convertRawProp(
141-
context,
142-
rawProps,
143-
"pointermove",
144-
sourceProps.pointerMove,
145-
{})),
128+
events(convertRawProp(context, rawProps, sourceProps.events, {})),
146129
collapsable(convertRawProp(
147130
context,
148131
rawProps,

ReactCommon/react/renderer/components/view/ViewProps.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps {
6161
EdgeInsets hitSlop{};
6262
bool onLayout{};
6363

64-
bool pointerEnter{};
65-
66-
bool pointerLeave{};
67-
68-
bool pointerMove{};
64+
ViewEvents events{};
6965

7066
bool collapsable{true};
7167

ReactCommon/react/renderer/components/view/ViewShadowNode.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ void ViewShadowNode::initialize() noexcept {
4848

4949
bool formsView = formsStackingContext ||
5050
isColorMeaningful(viewProps.backgroundColor) ||
51-
isColorMeaningful(viewProps.foregroundColor) || viewProps.pointerEnter ||
52-
viewProps.pointerLeave || viewProps.pointerMove ||
51+
isColorMeaningful(viewProps.foregroundColor) ||
52+
viewProps.events.bits.any() ||
5353
!(viewProps.yogaStyle.border() == YGStyle::Edges{}) ||
5454
!viewProps.testId.empty();
5555

ReactCommon/react/renderer/components/view/primitives.h

+41
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,54 @@
1111
#include <react/renderer/graphics/Color.h>
1212
#include <react/renderer/graphics/Geometry.h>
1313
#include <array>
14+
#include <bitset>
1415
#include <cmath>
1516

1617
namespace facebook {
1718
namespace react {
1819

1920
enum class PointerEventsMode { Auto, None, BoxNone, BoxOnly };
2021

22+
struct ViewEvents {
23+
std::bitset<32> bits{};
24+
25+
enum class Offset : std::size_t {
26+
// Pointer events
27+
PointerEnter = 0,
28+
PointerMove = 1,
29+
PointerLeave = 2,
30+
31+
// PanResponder callbacks
32+
MoveShouldSetResponder = 3,
33+
MoveShouldSetResponderCapture = 4,
34+
StartShouldSetResponder = 5,
35+
StartShouldSetResponderCapture = 6,
36+
ResponderGrant = 7,
37+
ResponderReject = 8,
38+
ResponderStart = 9,
39+
ResponderEnd = 10,
40+
ResponderRelease = 11,
41+
ResponderMove = 12,
42+
ResponderTerminate = 13,
43+
ResponderTerminationRequest = 14,
44+
ShouldBlockNativeResponder = 15,
45+
46+
// Touch events
47+
TouchStart = 16,
48+
TouchMove = 17,
49+
TouchEnd = 18,
50+
TouchCancel = 19,
51+
};
52+
53+
constexpr bool operator[](const Offset offset) const {
54+
return bits[static_cast<std::size_t>(offset)];
55+
}
56+
57+
std::bitset<32>::reference operator[](const Offset offset) {
58+
return bits[static_cast<std::size_t>(offset)];
59+
}
60+
};
61+
2162
enum class BackfaceVisibility { Auto, Visible, Hidden };
2263

2364
enum class BorderStyle { Solid, Dotted, Dashed };

0 commit comments

Comments
 (0)