Skip to content

Commit c744948

Browse files
huextratj-piasecki
authored andcommitted
fix(android): handles pointerEvents for Pressable component (#3927)
fixes: #3891 fixes: #3904 This PR fixes `pointerEvents` support for `Pressable` component from `react-native-gesture-handler` on Android. Waiting for iOS PR: #3925 as codegen is involved and a small iOS changes is needed Tested all `pointerEvents` modes on Android: - ✅ `pointerEvents="none"` - View and subviews don't receive touches - ✅ `pointerEvents="box-none"` - View doesn't receive touches, subviews do - ✅ `pointerEvents="box-only"` - View receives touches, subviews don't - ✅ `pointerEvents="auto"` - Default behavior works as expected I've used https://github.com/huextrat/repro-pressable-gh to test scenarios Tested on both old architecture (Paper) and new architecture (Fabric).
1 parent a313557 commit c744948

3 files changed

Lines changed: 30 additions & 4 deletions

File tree

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import androidx.core.view.children
2626
import com.facebook.react.R
2727
import com.facebook.react.module.annotations.ReactModule
2828
import com.facebook.react.uimanager.PixelUtil
29+
import com.facebook.react.uimanager.PointerEvents
30+
import com.facebook.react.uimanager.ReactPointerEventsView
2931
import com.facebook.react.uimanager.ThemedReactContext
3032
import com.facebook.react.uimanager.ViewGroupManager
3133
import com.facebook.react.uimanager.ViewManagerDelegate
@@ -131,6 +133,17 @@ class RNGestureHandlerButtonViewManager :
131133
view.isSoundEffectsEnabled = !touchSoundDisabled
132134
}
133135

136+
@ReactProp(name = ViewProps.POINTER_EVENTS)
137+
override fun setPointerEvents(view: ButtonViewGroup, pointerEvents: String?) {
138+
view.pointerEvents = when (pointerEvents) {
139+
"none" -> PointerEvents.NONE
140+
"box-none" -> PointerEvents.BOX_NONE
141+
"box-only" -> PointerEvents.BOX_ONLY
142+
"auto", null -> PointerEvents.AUTO
143+
else -> PointerEvents.AUTO
144+
}
145+
}
146+
134147
override fun onAfterUpdateTransaction(view: ButtonViewGroup) {
135148
super.onAfterUpdateTransaction(view)
136149

@@ -141,7 +154,8 @@ class RNGestureHandlerButtonViewManager :
141154

142155
class ButtonViewGroup(context: Context?) :
143156
ViewGroup(context),
144-
NativeViewGestureHandler.NativeViewGestureHandlerHook {
157+
NativeViewGestureHandler.NativeViewGestureHandlerHook,
158+
ReactPointerEventsView {
145159
// Using object because of handling null representing no value set.
146160
var rippleColor: Int? = null
147161
set(color) = withBackgroundUpdate {
@@ -199,6 +213,8 @@ class RNGestureHandlerButtonViewManager :
199213

200214
var exclusive = true
201215

216+
override var pointerEvents: PointerEvents = PointerEvents.AUTO
217+
202218
private var buttonBackgroundColor = Color.TRANSPARENT
203219
private var needBackgroundUpdate = false
204220
private var lastEventTime = -1L

packages/react-native-gesture-handler/apple/RNGestureHandlerButtonComponentView.mm

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,17 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
221221
_buttonView.hitTestEdgeInsets = UIEdgeInsetsMake(
222222
-newProps.hitSlop.top, -newProps.hitSlop.left, -newProps.hitSlop.bottom, -newProps.hitSlop.right);
223223

224+
// We need to cast to ViewProps to access the pointerEvents property with the correct type.
225+
// This is necessary because pointerEvents is redefined in the spec,
226+
// which shadows the base property with a different, incompatible type.
227+
const auto &newViewProps = static_cast<const ViewProps &>(newProps);
224228
if (!oldProps) {
225-
_buttonView.pointerEvents = RCTPointerEventsToEnum(newProps.pointerEvents);
229+
_buttonView.pointerEvents = RCTPointerEventsToEnum(newViewProps.pointerEvents);
226230
} else {
227231
const auto &oldButtonProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(oldProps);
228-
if (oldButtonProps.pointerEvents != newProps.pointerEvents) {
229-
_buttonView.pointerEvents = RCTPointerEventsToEnum(newProps.pointerEvents);
232+
const auto &oldViewProps = static_cast<const ViewProps &>(oldButtonProps);
233+
if (oldViewProps.pointerEvents != newViewProps.pointerEvents) {
234+
_buttonView.pointerEvents = RCTPointerEventsToEnum(newViewProps.pointerEvents);
230235
}
231236
}
232237

packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
} from 'react-native/Libraries/Types/CodegenTypes';
77
import type { ViewProps, ColorValue } from 'react-native';
88

9+
// @ts-ignore - Redefining pointerEvents with WithDefault for codegen, conflicts with ViewProps type but codegen needs it
910
interface NativeProps extends ViewProps {
1011
exclusive?: WithDefault<boolean, true>;
1112
foreground?: boolean;
@@ -17,6 +18,10 @@ interface NativeProps extends ViewProps {
1718
borderWidth?: Float;
1819
borderColor?: ColorValue;
1920
borderStyle?: WithDefault<string, 'solid'>;
21+
pointerEvents?: WithDefault<
22+
'box-none' | 'none' | 'box-only' | 'auto',
23+
'auto'
24+
>;
2025
}
2126

2227
export default codegenNativeComponent<NativeProps>('RNGestureHandlerButton');

0 commit comments

Comments
 (0)