From 4282e556686b3fa4f59780f898ee3ba94866cd48 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Tue, 12 Nov 2024 17:13:25 +0100 Subject: [PATCH 01/45] Bump Reanimated Update ios Renames: - `setGestureState` -> `setGestureState_DEPRECATED` (JSI injected name stays the same for compatibility) - `_setGestureStateNew` -> `_setGestureStateModern` Decorate UI runtime lazily Extract runtime decoration to shared code Prefix with underscore Expose `_setGestureStateAsync` to JS runtime Format c++ Fix header Don't try to decorate UI runtime without Reanimated and throw on failure iOS changes after review Changes to shared Continuously check whether ui runtime was decorated Make `createGestureHandler` synchronous and run on the JS thread Bump Reanimated Use Reanimated 4 beta Extract runtime decoration to shared code Bump reanimated to beta 5 --- apps/basic-example/ios/Podfile.lock | 214 +++++++++++++++++- .../src/main/jni/RNGestureHandlerModule.cpp | 1 + .../apple/RNGestureHandlerModule.mm | 1 + .../shared/RuntimeDecorator.cpp | 82 +++++++ .../shared/RuntimeDecorator.h | 12 + .../src/RNGestureHandlerModule.ts | 24 ++ .../handlers/gestures/gestureStateManager.ts | 20 ++ 7 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 packages/react-native-gesture-handler/shared/RuntimeDecorator.cpp create mode 100644 packages/react-native-gesture-handler/shared/RuntimeDecorator.h diff --git a/apps/basic-example/ios/Podfile.lock b/apps/basic-example/ios/Podfile.lock index 27ba11a5e7..0f50155419 100644 --- a/apps/basic-example/ios/Podfile.lock +++ b/apps/basic-example/ios/Podfile.lock @@ -2363,6 +2363,218 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - RNReanimated (4.0.0-beta.5): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNReanimated/reanimated (= 4.0.0-beta.5) + - RNWorklets + - SocketRocket + - Yoga + - RNReanimated/reanimated (4.0.0-beta.5): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNReanimated/reanimated/apple (= 4.0.0-beta.5) + - RNReanimated/reanimated/view (= 4.0.0-beta.5) + - RNWorklets + - SocketRocket + - Yoga + - RNReanimated/reanimated/apple (4.0.0-beta.5): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNWorklets + - SocketRocket + - Yoga + - RNReanimated/reanimated/view (4.0.0-beta.5): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNWorklets + - SocketRocket + - Yoga + - RNWorklets (0.3.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNWorklets/worklets (= 0.3.0) + - SocketRocket + - Yoga + - RNWorklets/worklets (0.3.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNWorklets/worklets/apple (= 0.3.0) + - SocketRocket + - Yoga + - RNWorklets/worklets/apple (0.3.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - SocketRocket (0.7.1) - Yoga (0.0.0) @@ -2678,7 +2890,7 @@ SPEC CHECKSUMS: RNReanimated: 25060745a200605462ff56cf488411db066631ce RNWorklets: 9bb08cb0ef718ce063f61ca18f95f57aec9b9673 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb + Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e PODFILE CHECKSUM: d05778d3a61b8d49242579ea0aa864580fbb1f64 diff --git a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp index faa2f2cd18..d51116b6e8 100644 --- a/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp +++ b/packages/react-native-gesture-handler/android/src/main/jni/RNGestureHandlerModule.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "RNGestureHandlerModule.h" diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm index 5d19446a44..53a654967f 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm @@ -117,6 +117,7 @@ - (void)initialize _manager = [[RNGestureHandlerManager alloc] initWithModuleRegistry:self.moduleRegistry viewRegistry:_viewRegistry_DEPRECATED]; _operations = [NSMutableArray new]; + _runtime = nullptr; } #else - (void)setBridge:(RCTBridge *)bridge diff --git a/packages/react-native-gesture-handler/shared/RuntimeDecorator.cpp b/packages/react-native-gesture-handler/shared/RuntimeDecorator.cpp new file mode 100644 index 0000000000..a461fb2aa5 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/RuntimeDecorator.cpp @@ -0,0 +1,82 @@ +#ifndef ANDROID +#include +#include +#endif + +#include + +#include "RuntimeDecorator.h" + +namespace gesturehandler { + using namespace facebook; + using namespace facebook::react; + + void RuntimeDecorator::installJSRuntimeBindings(jsi::Runtime& rnRuntime) { + auto isViewFlatteningDisabled = jsi::Function::createFromHostFunction( + rnRuntime, + jsi::PropNameID::forAscii(rnRuntime, "isViewFlatteningDisabled"), + 1, + [](jsi::Runtime &runtime, + const jsi::Value &, + const jsi::Value *args, + size_t count) -> jsi::Value { + if (!args[0].isObject()) { + return jsi::Value::null(); + } + + const auto shadowNode = shadowNodeFromValue(runtime, args[0]); + +#ifndef ANDROID + if (dynamic_pointer_cast(shadowNode)) { + return jsi::Value(true); + } + + if (dynamic_pointer_cast(shadowNode)) { + return jsi::Value(true); + } +#endif + + const auto isViewFlatteningDisabled = shadowNode->getTraits().check(ShadowNodeTraits::FormsStackingContext); + + // This is done using component names instead of type checking because + // of duplicate symbols for RN types, which prevent RTTI from working. + const auto componentName = shadowNode->getComponentName(); + const auto isTextComponent = strcmp(componentName, "Paragraph") == 0 || strcmp(componentName, "Text") == 0; + + return jsi::Value(isViewFlatteningDisabled || isTextComponent); + }); + + rnRuntime.global().setProperty( + rnRuntime, "isViewFlatteningDisabled", std::move(isViewFlatteningDisabled)); + } + + bool RuntimeDecorator::installUIRuntimeBindings(jsi::Runtime& rnRuntime, std::function&& setGestureState) { + const auto runtimeHolder = rnRuntime.global().getProperty(rnRuntime, "_WORKLET_RUNTIME"); + + if (runtimeHolder.isUndefined()) { + return false; + } + + const auto arrayBufferValue = runtimeHolder.getObject(rnRuntime).getArrayBuffer(rnRuntime).data(rnRuntime); + const auto uiRuntimeAddress = reinterpret_cast(&arrayBufferValue[0]); + jsi::Runtime &uiRuntime = *reinterpret_cast(*uiRuntimeAddress); + + auto setGestureStateSync = jsi::Function::createFromHostFunction( + uiRuntime, + jsi::PropNameID::forAscii(uiRuntime, "_setGestureStateSync"), + 2, + [setGestureState](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) -> jsi::Value { + if (count == 2) { + const auto handlerTag = static_cast(args[0].asNumber()); + const auto state = static_cast(args[1].asNumber()); + + // TODO: expose to JS and dispatch to UI thread if called on JS? + setGestureState(handlerTag, state); + } + return jsi::Value::undefined(); + }); + + uiRuntime.global().setProperty(uiRuntime, "_setGestureStateSync", std::move(setGestureStateSync)); + return true; + } +} // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/shared/RuntimeDecorator.h b/packages/react-native-gesture-handler/shared/RuntimeDecorator.h new file mode 100644 index 0000000000..cd6f7498bf --- /dev/null +++ b/packages/react-native-gesture-handler/shared/RuntimeDecorator.h @@ -0,0 +1,12 @@ +#pragma once + +namespace gesturehandler { + using namespace facebook; + using namespace facebook::react; + + class RuntimeDecorator { + public: + static void installJSRuntimeBindings(jsi::Runtime& rnRuntime); + static bool installUIRuntimeBindings(jsi::Runtime& rnRuntime, std::function&& setGestureState); + }; +} // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.ts b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.ts index b32050b354..d4011815f0 100644 --- a/packages/react-native-gesture-handler/src/RNGestureHandlerModule.ts +++ b/packages/react-native-gesture-handler/src/RNGestureHandlerModule.ts @@ -3,3 +3,27 @@ import Module from './specs/NativeRNGestureHandlerModule'; export default Module; + +declare const global: { + _WORKLET_RUNTIME: ArrayBuffer; +}; + +function tryInstallUIBindings() { + if (global._WORKLET_RUNTIME) { + console.log('Installing Gesture Handler UI bindings'); + Module.installUIRuntimeBindings(); + } else { + setTimeout(() => { + console.log('Waiting for worklet runtime to be available'); + tryInstallUIBindings(); + }, 1); + } +} + +try { + require('react-native-reanimated'); + console.log('Reanimated is available'); + tryInstallUIBindings(); +} catch (error) { + console.error("Couldn't install Gesture Handler UI bindings", error); +} diff --git a/packages/react-native-gesture-handler/src/handlers/gestures/gestureStateManager.ts b/packages/react-native-gesture-handler/src/handlers/gestures/gestureStateManager.ts index 2faa4250d7..dcdf638875 100644 --- a/packages/react-native-gesture-handler/src/handlers/gestures/gestureStateManager.ts +++ b/packages/react-native-gesture-handler/src/handlers/gestures/gestureStateManager.ts @@ -26,6 +26,26 @@ const wrappedSetGestureState = (handlerTag: number, state: State) => { } }; +// ui runtime global +declare const global: { + _setGestureStateNew: (handlerTag: number, state: State) => void; +}; + +const wrappedSetGestureState = (handlerTag: number, state: State) => { + 'worklet'; + + if (REANIMATED_AVAILABLE) { + // When Reanimated is available, setGestureState should be defined + if (global._setGestureStateNew) { + global._setGestureStateNew(handlerTag, state); + } else if (setGestureState) { + setGestureState(handlerTag, state); + } + } else { + console.warn(warningMessage); + } +}; + function create(handlerTag: number): GestureStateManagerType { 'worklet'; return { From 58ce579c22b97b09425c3c60cba58646930ca513 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 20 Feb 2025 17:31:02 +0100 Subject: [PATCH 02/45] Bootstrap native detector --- .../gesturehandler/RNGestureHandlerPackage.kt | 10 ++- .../react/RNGestureHandlerDetectorView.kt | 10 +++ .../RNGestureHandlerDetectorViewManager.kt | 36 +++++++++++ .../apple/RNGestureHandlerDetector.h | 19 ++++++ .../apple/RNGestureHandlerDetector.mm | 63 +++++++++++++++++++ .../apple/RNGestureHandlerDetectorManager.m | 14 +++++ .../react-native-gesture-handler/package.json | 3 +- .../src/NativeDetector.tsx | 16 +++++ .../react-native-gesture-handler/src/index.ts | 3 + ...RNGestureHandlerDetectorNativeComponent.ts | 9 +++ 10 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt create mode 100644 packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt create mode 100644 packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h create mode 100644 packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm create mode 100644 packages/react-native-gesture-handler/apple/RNGestureHandlerDetectorManager.m create mode 100644 packages/react-native-gesture-handler/src/NativeDetector.tsx create mode 100644 packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt index e271a3e2f7..981e467b28 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/RNGestureHandlerPackage.kt @@ -11,6 +11,7 @@ import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager +import com.swmansion.gesturehandler.react.RNGestureHandlerDetectorViewManager import com.swmansion.gesturehandler.react.RNGestureHandlerModule import com.swmansion.gesturehandler.react.RNGestureHandlerRootViewManager @@ -30,11 +31,18 @@ class RNGestureHandlerPackage : RNGestureHandlerButtonViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec { RNGestureHandlerButtonViewManager() }, + RNGestureHandlerDetectorViewManager.REACT_CLASS to ModuleSpec.viewManagerSpec { + RNGestureHandlerDetectorViewManager() + } ) } override fun createViewManagers(reactContext: ReactApplicationContext) = - listOf>(RNGestureHandlerRootViewManager(), RNGestureHandlerButtonViewManager()) + listOf>( + RNGestureHandlerRootViewManager(), + RNGestureHandlerButtonViewManager(), + RNGestureHandlerDetectorViewManager() + ) override fun getViewManagerNames(reactContext: ReactApplicationContext) = viewManagers.keys.toList() diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt new file mode 100644 index 0000000000..90e1be9c9b --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt @@ -0,0 +1,10 @@ +package com.swmansion.gesturehandler.react + +import android.content.Context +import com.facebook.react.views.view.ReactViewGroup + +class RNGestureHandlerDetectorView: ReactViewGroup { + constructor(context: Context?) : super(context) { + + } +} diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt new file mode 100644 index 0000000000..5fad1c8b23 --- /dev/null +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorViewManager.kt @@ -0,0 +1,36 @@ +package com.swmansion.gesturehandler.react + +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.module.annotations.ReactModule +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.ViewGroupManager +import com.facebook.react.uimanager.ViewManagerDelegate +import com.facebook.react.viewmanagers.RNGestureHandlerDetectorManagerDelegate +import com.facebook.react.viewmanagers.RNGestureHandlerDetectorManagerInterface + +@ReactModule(name = RNGestureHandlerDetectorViewManager.REACT_CLASS) +class RNGestureHandlerDetectorViewManager : + ViewGroupManager(), + RNGestureHandlerDetectorManagerInterface { + private val mDelegate: ViewManagerDelegate + + init { + mDelegate = RNGestureHandlerDetectorManagerDelegate(this) + } + + override fun getDelegate(): ViewManagerDelegate { + return mDelegate + } + + override fun getName() = REACT_CLASS + + override fun createViewInstance(reactContext: ThemedReactContext) = RNGestureHandlerDetectorView(reactContext) + + companion object { + const val REACT_CLASS = "RNGestureHandlerDetector" + } + + override fun setHandlerTags(view: RNGestureHandlerDetectorView?, value: ReadableArray?) { + + } +} diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h new file mode 100644 index 0000000000..57ec9fedc6 --- /dev/null +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h @@ -0,0 +1,19 @@ +#ifdef RCT_NEW_ARCH_ENABLED + +#if !TARGET_OS_OSX +#import +#else +#import +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RNGestureHandlerDetector : RCTViewComponentView + +@end + +NS_ASSUME_NONNULL_END + +#endif // RCT_NEW_ARCH_ENABLED diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm new file mode 100644 index 0000000000..9eb116542e --- /dev/null +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -0,0 +1,63 @@ +#ifdef RCT_NEW_ARCH_ENABLED + +#import "RNGestureHandlerDetector.h" + +#import +#import + +#import +#import +#import +#import + +using namespace facebook::react; + +@interface RNGestureHandlerDetector () +@end + +@implementation RNGestureHandlerDetector + +#if TARGET_OS_OSX ++ (BOOL)shouldBeRecycled +{ + return NO; +} +#endif + +// Needed because of this: https://github.com/facebook/react-native/pull/37274 ++ (void)load +{ + [super load]; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + static const auto defaultProps = std::make_shared(); + _props = defaultProps; + } + + return self; +} + +#pragma mark - RCTComponentViewProtocol + ++ (ComponentDescriptorProvider)componentDescriptorProvider +{ + return concreteComponentDescriptorProvider(); +} + +- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps +{ + const auto &newProps = *std::static_pointer_cast(props); + + [super updateProps:props oldProps:oldProps]; +} +@end + +Class RNGestureHandlerDetectorCls(void) +{ + return RNGestureHandlerDetector.class; +} + +#endif // RCT_NEW_ARCH_ENABLED diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetectorManager.m b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetectorManager.m new file mode 100644 index 0000000000..cbccb158f4 --- /dev/null +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetectorManager.m @@ -0,0 +1,14 @@ +#import +#import +#import + +@interface RNGestureHandlerDetectorManager : RCTViewManager +@end + +@implementation RNGestureHandlerDetectorManager + +RCT_EXPORT_MODULE(RNGestureHandlerDetector) + +RCT_EXPORT_VIEW_PROPERTY(handlerTags, NSArray) + +@end diff --git a/packages/react-native-gesture-handler/package.json b/packages/react-native-gesture-handler/package.json index 56ecd61260..9397bca506 100644 --- a/packages/react-native-gesture-handler/package.json +++ b/packages/react-native-gesture-handler/package.json @@ -135,7 +135,8 @@ }, "ios": { "componentProvider": { - "RNGestureHandlerButton": "RNGestureHandlerButtonComponentView" + "RNGestureHandlerButton": "RNGestureHandlerButtonComponentView", + "RNGestureHandlerDetector": "RNGestureHandlerDetector" } } }, diff --git a/packages/react-native-gesture-handler/src/NativeDetector.tsx b/packages/react-native-gesture-handler/src/NativeDetector.tsx new file mode 100644 index 0000000000..17784e94d4 --- /dev/null +++ b/packages/react-native-gesture-handler/src/NativeDetector.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import type { NativeProps } from './specs/RNGestureHandlerDetectorNativeComponent'; +import RNGestureHandlerDetectorNativeComponent from './specs/RNGestureHandlerDetectorNativeComponent'; + +export interface NativeDetectorProps extends NativeProps { + children?: React.ReactNode; +} + +export function NativeDetector({ handlerTags, ...rest }: NativeDetectorProps) { + return ( + + ); +} diff --git a/packages/react-native-gesture-handler/src/index.ts b/packages/react-native-gesture-handler/src/index.ts index 7ad400a7a7..737dd014bb 100644 --- a/packages/react-native-gesture-handler/src/index.ts +++ b/packages/react-native-gesture-handler/src/index.ts @@ -161,4 +161,7 @@ export type { } from './components/DrawerLayout'; export { default as DrawerLayout } from './components/DrawerLayout'; +export type { NativeDetectorProps } from './NativeDetector'; +export { NativeDetector } from './NativeDetector'; + initialize(); diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts new file mode 100644 index 0000000000..7376077835 --- /dev/null +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -0,0 +1,9 @@ +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import type { Int32 } from 'react-native/Libraries/Types/CodegenTypes'; +import type { ViewProps } from 'react-native'; + +export interface NativeProps extends ViewProps { + handlerTags: Int32[]; +} + +export default codegenNativeComponent('RNGestureHandlerDetector'); From ee36d965469ea4b6b9915afb6a7c1f858706acbe Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 20 Feb 2025 17:55:48 +0100 Subject: [PATCH 03/45] Setup custom shadow node iOS --- apps/macos-example/macos/Podfile.lock | 22 ++++++++++++ .../apple/RNGestureHandlerDetector.mm | 2 +- ...estureHandlerDetectorComponentDescriptor.h | 31 ++++++++++++++++ .../RNGestureHandlerDetectorShadowNode.cpp | 17 +++++++++ .../RNGestureHandlerDetectorShadowNode.h | 36 +++++++++++++++++++ .../RNGestureHandlerDetectorState.h | 29 +++++++++++++++ ...RNGestureHandlerDetectorNativeComponent.ts | 4 ++- 7 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h create mode 100644 packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp create mode 100644 packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h create mode 100644 packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h diff --git a/apps/macos-example/macos/Podfile.lock b/apps/macos-example/macos/Podfile.lock index 35c6fe377c..175b9bcc71 100644 --- a/apps/macos-example/macos/Podfile.lock +++ b/apps/macos-example/macos/Podfile.lock @@ -1504,6 +1504,28 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - RNGestureHandler/shared (= 2.23.1) + - Yoga + - RNGestureHandler/shared (2.23.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core - Yoga - RNReanimated (3.18.0): - DoubleConversion diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm index 9eb116542e..17d82a5e17 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm @@ -1,11 +1,11 @@ #ifdef RCT_NEW_ARCH_ENABLED #import "RNGestureHandlerDetector.h" +#import "RNGestureHandlerDetectorComponentDescriptor.h" #import #import -#import #import #import #import diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h new file mode 100644 index 0000000000..428870ef5d --- /dev/null +++ b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h @@ -0,0 +1,31 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateComponentDescriptorH.js + */ + +#pragma once + +#include + +#include "RNGestureHandlerDetectorShadowNode.h" + +namespace facebook::react { + +class RNGestureHandlerDetectorComponentDescriptor final : public ConcreteComponentDescriptor { + using ConcreteComponentDescriptor::ConcreteComponentDescriptor; + void adopt(ShadowNode &shadowNode) const override { + react_native_assert(dynamic_cast(&shadowNode)); + auto detectorShadowNode = dynamic_cast(&shadowNode); + + detectorShadowNode->setSize({.width = 300, .height = 300}); + + ConcreteComponentDescriptor::adopt(shadowNode); + } +}; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp new file mode 100644 index 0000000000..60a7461d42 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp @@ -0,0 +1,17 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeCpp.js + */ + +#include "RNGestureHandlerDetectorShadowNode.h" + +namespace facebook::react { + +extern const char RNGestureHandlerDetectorComponentName[] = "RNGestureHandlerDetector"; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h new file mode 100644 index 0000000000..948c944bf8 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h @@ -0,0 +1,36 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateShadowNodeH.js + */ + +#pragma once + +#include +#include +#include +#include + +#include "RNGestureHandlerDetectorState.h" + +namespace facebook::react { + +JSI_EXPORT extern const char RNGestureHandlerDetectorComponentName[]; + +/* + * `ShadowNode` for component. + */ +class RNGestureHandlerDetectorShadowNode final : public ConcreteViewShadowNode< + RNGestureHandlerDetectorComponentName, + RNGestureHandlerDetectorProps, + RNGestureHandlerDetectorEventEmitter, + RNGestureHandlerDetectorState> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; +}; + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h new file mode 100644 index 0000000000..7a5dba7b70 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h @@ -0,0 +1,29 @@ +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateStateH.js + */ +#pragma once + +#ifdef ANDROID +#include +#endif + +namespace facebook::react { + +class RNGestureHandlerDetectorState { +public: + RNGestureHandlerDetectorState() = default; + +#ifdef ANDROID + RNGestureHandlerDetectorState(RNGestureHandlerDetectorState const &previousState, folly::dynamic data){}; + folly::dynamic getDynamic() const { + return {}; + }; +#endif +}; + +} // namespace facebook::react \ No newline at end of file diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 7376077835..8752ed4967 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -6,4 +6,6 @@ export interface NativeProps extends ViewProps { handlerTags: Int32[]; } -export default codegenNativeComponent('RNGestureHandlerDetector'); +export default codegenNativeComponent('RNGestureHandlerDetector', { + interfaceOnly: true, +}); From a2348c1eb57afaea7467c096326be2c3bbffac46 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 20 Feb 2025 18:29:20 +0100 Subject: [PATCH 04/45] Setup custom node on Android --- .../android/CMakeLists.txt | 25 ++++ .../android/src/main/jni/CMakeLists.txt | 6 +- .../react-native-gesture-handler/package.json | 3 +- .../react-native.config.js | 10 ++ .../shared/runtime/RuntimeDecorator.cpp | 124 ++++++++++++++++++ .../shared/runtime/RuntimeDecorator.h | 17 +++ .../ComponentDescriptors.h | 27 ++++ ...estureHandlerDetectorComponentDescriptor.h | 0 .../RNGestureHandlerDetectorShadowNode.cpp | 0 .../RNGestureHandlerDetectorShadowNode.h | 0 .../RNGestureHandlerDetectorState.h | 0 11 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 packages/react-native-gesture-handler/android/CMakeLists.txt create mode 100644 packages/react-native-gesture-handler/react-native.config.js create mode 100644 packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.cpp create mode 100644 packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.h create mode 100644 packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h rename packages/react-native-gesture-handler/shared/{ => shadowNodes}/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h (100%) rename packages/react-native-gesture-handler/shared/{ => shadowNodes}/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp (100%) rename packages/react-native-gesture-handler/shared/{ => shadowNodes}/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h (100%) rename packages/react-native-gesture-handler/shared/{ => shadowNodes}/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h (100%) diff --git a/packages/react-native-gesture-handler/android/CMakeLists.txt b/packages/react-native-gesture-handler/android/CMakeLists.txt new file mode 100644 index 0000000000..3499b7420e --- /dev/null +++ b/packages/react-native-gesture-handler/android/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.13) +project(GestureHandlerCodegen) + +set(CMAKE_VERBOSE_MAKEFILE on) +set(CMAKE_CXX_STANDARD 20) + +file(GLOB_RECURSE rn_gesture_handler_SRCS CONFIGURE_DEPENDS ../shared/shadowNodes/*.cpp) +file(GLOB_RECURSE rn_gesture_handler_codegen_SRCS CONFIGURE_DEPENDS ./build/generated/source/codegen/jni/*.cpp) + +add_library( + react_codegen_rngesturehandler_codegen + SHARED + ${rn_gesture_handler_SRCS} + ${rn_gesture_handler_codegen_SRCS} +) + +target_include_directories(react_codegen_rngesturehandler_codegen PUBLIC ../shared/shadowNodes) +target_include_directories(react_codegen_rngesturehandler_codegen PUBLIC ./build/generated/source/codegen/jni) + +target_link_libraries( + react_codegen_rngesturehandler_codegen + fbjni + jsi + reactnative +) diff --git a/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt b/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt index 7b9566f4bd..a039a2fdb3 100644 --- a/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt +++ b/packages/react-native-gesture-handler/android/src/main/jni/CMakeLists.txt @@ -12,8 +12,8 @@ set(PACKAGE_NAME "gesturehandler") set(RNGH_DIR "${CMAKE_SOURCE_DIR}/../../../../") set(REACT_ANDROID_DIR "${REACT_NATIVE_DIR}/ReactAndroid") -file(GLOB_RECURSE gesture_handler_SRCS CONFIGURE_DEPENDS "./*.cpp") -file(GLOB_RECURSE gesture_handler_shared_SRCS CONFIGURE_DEPENDS "${RNGH_DIR}/shared/*.cpp") +file(GLOB_RECURSE gesture_handler_SRCS CONFIGURE_DEPENDS ./*.cpp) +file(GLOB_RECURSE gesture_handler_shared_SRCS CONFIGURE_DEPENDS "${RNGH_DIR}/shared/runtime/*.cpp") include(${REACT_ANDROID_DIR}/cmake-utils/folly-flags.cmake) add_compile_options(${folly_FLAGS}) @@ -28,7 +28,7 @@ target_include_directories( ${PACKAGE_NAME} PUBLIC "${CMAKE_SOURCE_DIR}" - "${RNGH_DIR}/shared" + "${RNGH_DIR}/shared/runtime" PRIVATE "${REACT_NATIVE_DIR}/ReactCommon" ) diff --git a/packages/react-native-gesture-handler/package.json b/packages/react-native-gesture-handler/package.json index 9397bca506..be74ea7351 100644 --- a/packages/react-native-gesture-handler/package.json +++ b/packages/react-native-gesture-handler/package.json @@ -55,7 +55,8 @@ "ReanimatedDrawerLayout/", "README.md", "jestSetup.js", - "RNGestureHandler.podspec" + "RNGestureHandler.podspec", + "react-native.config.js" ], "repository": { "type": "git", diff --git a/packages/react-native-gesture-handler/react-native.config.js b/packages/react-native-gesture-handler/react-native.config.js new file mode 100644 index 0000000000..4ec41154cd --- /dev/null +++ b/packages/react-native-gesture-handler/react-native.config.js @@ -0,0 +1,10 @@ +module.exports = { + dependency: { + platforms: { + android: { + componentDescriptors: ['RNGestureHandlerDetectorComponentDescriptor'], + cmakeListsPath: './CMakeLists.txt', + }, + }, + }, +}; diff --git a/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.cpp b/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.cpp new file mode 100644 index 0000000000..7171ec2285 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.cpp @@ -0,0 +1,124 @@ +#ifndef ANDROID +#include +#include +#endif + +#include + +#include "RNGHRuntimeDecorator.h" + +namespace gesturehandler { + +using namespace facebook; +using namespace facebook::react; + +void RNGHRuntimeDecorator::installRNRuntimeBindings( + jsi::Runtime &rnRuntime, + std::function &&setGestureState) { + const auto isViewFlatteningDisabled = jsi::Function::createFromHostFunction( + rnRuntime, + jsi::PropNameID::forAscii(rnRuntime, "_isViewFlatteningDisabled"), + 1, + [](jsi::Runtime &runtime, + const jsi::Value &, + const jsi::Value *args, + size_t count) -> jsi::Value { + if (!args[0].isObject()) { + return jsi::Value::null(); + } + + const auto shadowNode = shadowNodeFromValue(runtime, args[0]); + +#ifndef ANDROID + if (dynamic_pointer_cast(shadowNode)) { + return jsi::Value(true); + } + + if (dynamic_pointer_cast(shadowNode)) { + return jsi::Value(true); + } +#endif + + const auto isFormsStackingContext = shadowNode->getTraits().check( + ShadowNodeTraits::FormsStackingContext); + + // This is done using component names instead of type checking because + // of duplicate symbols for RN types, which prevent RTTI from working. + const auto &componentName = shadowNode->getComponentName(); + const auto isTextOrParagraphComponent = + strcmp(componentName, "Paragraph") == 0 || + strcmp(componentName, "Text") == 0; + + return jsi::Value(isFormsStackingContext || isTextOrParagraphComponent); + }); + + rnRuntime.global().setProperty( + rnRuntime, + "_isViewFlatteningDisabled", + std::move(isViewFlatteningDisabled)); + + auto setGestureStateAsync = jsi::Function::createFromHostFunction( + rnRuntime, + jsi::PropNameID::forAscii(rnRuntime, "_setGestureStateAsync"), + 2, + [setGestureState]( + jsi::Runtime &rt, + const jsi::Value &, + const jsi::Value *args, + size_t count) -> jsi::Value { + if (count == 2) { + const auto handlerTag = static_cast(args[0].asNumber()); + const auto state = static_cast(args[1].asNumber()); + + setGestureState(handlerTag, state); + } + return jsi::Value::undefined(); + }); + + rnRuntime.global().setProperty( + rnRuntime, "_setGestureStateAsync", std::move(setGestureStateAsync)); +} + +bool RNGHRuntimeDecorator::installUIRuntimeBindings( + jsi::Runtime &rnRuntime, + std::function &&setGestureState) { + const auto runtimeHolder = + rnRuntime.global().getProperty(rnRuntime, "_WORKLET_RUNTIME"); + + if (runtimeHolder.isUndefined()) { + return false; + } + + const auto arrayBufferValue = + runtimeHolder.getObject(rnRuntime).getArrayBuffer(rnRuntime).data( + rnRuntime); + const auto uiRuntimeAddress = + reinterpret_cast(&arrayBufferValue[0]); + jsi::Runtime &uiRuntime = + *reinterpret_cast(*uiRuntimeAddress); + + auto setGestureStateSync = jsi::Function::createFromHostFunction( + uiRuntime, + jsi::PropNameID::forAscii(uiRuntime, "_setGestureStateSync"), + 2, + [setGestureState]( + jsi::Runtime &rt, + const jsi::Value &, + const jsi::Value *args, + size_t count) -> jsi::Value { + if (count == 2) { + const auto handlerTag = static_cast(args[0].asNumber()); + const auto state = static_cast(args[1].asNumber()); + + setGestureState(handlerTag, state); + } + return jsi::Value::undefined(); + }); + + uiRuntime.global().setProperty( + uiRuntime, "_setGestureStateSync", std::move(setGestureStateSync)); + + return true; +} + +} // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.h b/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.h new file mode 100644 index 0000000000..17be4fd990 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/runtime/RuntimeDecorator.h @@ -0,0 +1,17 @@ +#pragma once +#include + +namespace gesturehandler { +using namespace facebook; + +class RNGHRuntimeDecorator { + public: + static void installRNRuntimeBindings( + jsi::Runtime &rnRuntime, + std::function &&setGestureState); + static bool installUIRuntimeBindings( + jsi::Runtime &rnRuntime, + std::function &&setGestureState); +}; + +} // namespace gesturehandler diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h new file mode 100644 index 0000000000..ac4aceecc0 --- /dev/null +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h @@ -0,0 +1,27 @@ + +/** + * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). + * + * Do not edit this file as changes may cause incorrect behavior and will be lost + * once the code is regenerated. + * + * @generated by codegen project: GenerateComponentDescriptorH.js + */ + +#pragma once + +#include +#include +#include + +#include + +namespace facebook::react { + +using RNGestureHandlerButtonComponentDescriptor = ConcreteComponentDescriptor; +using RNGestureHandlerRootViewComponentDescriptor = ConcreteComponentDescriptor; + +void FBReactNativeSpec_registerComponentDescriptorsFromCodegen( + std::shared_ptr registry); + +} // namespace facebook::react diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h similarity index 100% rename from packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h rename to packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorComponentDescriptor.h diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp similarity index 100% rename from packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp rename to packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h similarity index 100% rename from packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h rename to packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h diff --git a/packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h similarity index 100% rename from packages/react-native-gesture-handler/shared/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h rename to packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorState.h From 46dba42135395d374538b36f59a5302f2fe73c4c Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Thu, 20 Feb 2025 18:52:51 +0100 Subject: [PATCH 05/45] Setup detector to match child's size --- .../RNGestureHandlerDetectorShadowNode.h | 41 ++++++++++++++++++- .../src/NativeDetector.tsx | 16 ++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h index 948c944bf8..73d25494fb 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "RNGestureHandlerDetectorState.h" @@ -29,8 +30,44 @@ class RNGestureHandlerDetectorShadowNode final : public ConcreteViewShadowNode< RNGestureHandlerDetectorProps, RNGestureHandlerDetectorEventEmitter, RNGestureHandlerDetectorState> { - public: - using ConcreteViewShadowNode::ConcreteViewShadowNode; +public: + RNGestureHandlerDetectorShadowNode( + const ShadowNodeFragment& fragment, + const ShadowNodeFamily::Shared& family, + ShadowNodeTraits traits) + : ConcreteViewShadowNode(fragment, family, traits) { + initialize(); + } + + RNGestureHandlerDetectorShadowNode( + const ShadowNode& sourceShadowNode, + const ShadowNodeFragment& fragment) + : ConcreteViewShadowNode(sourceShadowNode, fragment) { + initialize(); + } + + void initialize() { + // Disable forcing view flattening + ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView); + } + + void layout(LayoutContext layoutContext) override { + YogaLayoutableShadowNode::layout(layoutContext); + // TODO: consider allowing more than one child and doing bounding box + react_native_assert(getChildren().size() == 1); + + auto child = std::static_pointer_cast(getChildren()[0]); + auto mutableChild = std::const_pointer_cast(child); + + // TODO: figure out the correct way to setup metrics between detector and the child + auto metrics = child->getLayoutMetrics(); + metrics.frame = child->getLayoutMetrics().frame; + setLayoutMetrics(metrics); + + auto childmetrics = child->getLayoutMetrics(); + childmetrics.frame.origin = Point{}; + mutableChild->setLayoutMetrics(childmetrics); + } }; } // namespace facebook::react diff --git a/packages/react-native-gesture-handler/src/NativeDetector.tsx b/packages/react-native-gesture-handler/src/NativeDetector.tsx index 17784e94d4..3f598a8316 100644 --- a/packages/react-native-gesture-handler/src/NativeDetector.tsx +++ b/packages/react-native-gesture-handler/src/NativeDetector.tsx @@ -1,16 +1,26 @@ import React from 'react'; import type { NativeProps } from './specs/RNGestureHandlerDetectorNativeComponent'; import RNGestureHandlerDetectorNativeComponent from './specs/RNGestureHandlerDetectorNativeComponent'; +import { StyleSheet } from 'react-native'; export interface NativeDetectorProps extends NativeProps { children?: React.ReactNode; } -export function NativeDetector({ handlerTags, ...rest }: NativeDetectorProps) { +export function NativeDetector({ handlerTags, children }: NativeDetectorProps) { return ( + style={styles.detector}> + {children} + ); } + +const styles = StyleSheet.create({ + detector: { + display: 'contents', + // TODO: remove, debug info only + backgroundColor: 'red', + }, +}); From 264f45c7a04947963c33b0e1e81fd41f14e13761 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Sat, 22 Feb 2025 12:07:52 +0100 Subject: [PATCH 06/45] Add events to the spec --- ...RNGestureHandlerDetectorNativeComponent.ts | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts index 8752ed4967..46b59c89e6 100644 --- a/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts +++ b/packages/react-native-gesture-handler/src/specs/RNGestureHandlerDetectorNativeComponent.ts @@ -1,8 +1,56 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; -import type { Int32 } from 'react-native/Libraries/Types/CodegenTypes'; +import type { + Int32, + DirectEventHandler, + UnsafeMixed, + Double, +} from 'react-native/Libraries/Types/CodegenTypes'; import type { ViewProps } from 'react-native'; +type GestureHandlerEvent = Readonly<{ + handlerTag: Int32; + numberOfPointers: Int32; + state: Int32; + pointerType: Int32; + handlerData: UnsafeMixed; +}>; + +type GestureHandlerStateChangeEvent = Readonly<{ + handlerTag: Int32; + numberOfPointers: Int32; + state: Int32; + oldState: Int32; + pointerType: Int32; + handlerData: UnsafeMixed; +}>; + +type GestureHandlerTouchEvent = Readonly<{ + handlerTag: Int32; + numberOfTouches: Int32; + state: Int32; + eventType: Int32; + allTouches: { + id: Int32; + x: Double; + y: Double; + absoluteX: Double; + absoluteY: Double; + }[]; + changedTouches: { + id: Int32; + x: Double; + y: Double; + absoluteX: Double; + absoluteY: Double; + }[]; + pointerType: Int32; +}>; + export interface NativeProps extends ViewProps { + onGestureEvent?: DirectEventHandler; + onGestureHandlerStateChange?: DirectEventHandler; + onGestureHandlerTouchEvent?: DirectEventHandler; + handlerTags: Int32[]; } From fe50aa3164a0dc5371c5688771b80513ca2b3378 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Sat, 22 Feb 2025 13:05:57 +0100 Subject: [PATCH 07/45] Allow handlers to be detached without destroing them --- .../NativeRNGestureHandlerModuleSpec.java | 4 ++++ .../gesturehandler/react/RNGestureHandlerModule.kt | 6 ++++++ .../react/RNGestureHandlerRegistry.kt | 13 ++++++++++--- .../apple/RNGestureHandlerManager.h | 2 ++ .../apple/RNGestureHandlerManager.mm | 5 +++++ .../apple/RNGestureHandlerModule.mm | 7 +++++++ .../apple/RNGestureHandlerRegistry.h | 1 + .../apple/RNGestureHandlerRegistry.m | 6 ++++++ .../src/specs/NativeRNGestureHandlerModule.ts | 2 ++ 9 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/react-native-gesture-handler/android/paper/src/main/java/com/swmansion/gesturehandler/NativeRNGestureHandlerModuleSpec.java b/packages/react-native-gesture-handler/android/paper/src/main/java/com/swmansion/gesturehandler/NativeRNGestureHandlerModuleSpec.java index 3fedeea9a9..76ab582e16 100644 --- a/packages/react-native-gesture-handler/android/paper/src/main/java/com/swmansion/gesturehandler/NativeRNGestureHandlerModuleSpec.java +++ b/packages/react-native-gesture-handler/android/paper/src/main/java/com/swmansion/gesturehandler/NativeRNGestureHandlerModuleSpec.java @@ -49,6 +49,10 @@ public NativeRNGestureHandlerModuleSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void attachGestureHandler(double handlerTag, double newView, double actionType); + @ReactMethod + @DoNotStrip + public abstract void detachGestureHandler(double handlerTag); + @ReactMethod @DoNotStrip public abstract void updateGestureHandler(double handlerTag, ReadableMap newConfig); diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt index 8b781ac0ed..a382b109eb 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerModule.kt @@ -106,6 +106,12 @@ class RNGestureHandlerModule(reactContext: ReactApplicationContext?) : registry.dropHandler(handlerTag) } + @ReactMethod + override fun detachGestureHandler(handlerTagDouble: Double) { + val handlerTag = handlerTagDouble.toInt() + registry.detachHandler(handlerTag) + } + @ReactMethod override fun handleSetJSResponder(viewTagDouble: Double, blockNativeResponder: Boolean) { val viewTag = viewTagDouble.toInt() diff --git a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.kt b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.kt index bfc9c433d6..e9691c3b8b 100644 --- a/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.kt +++ b/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerRegistry.kt @@ -24,7 +24,7 @@ class RNGestureHandlerRegistry : GestureHandlerRegistry { fun attachHandlerToView(handlerTag: Int, viewTag: Int, actionType: Int): Boolean { val handler = handlers[handlerTag] return handler?.let { - detachHandler(handler) + detachHandlerInternal(handler) handler.actionType = actionType registerHandlerForViewWithTag(viewTag, handler) true @@ -48,7 +48,7 @@ class RNGestureHandlerRegistry : GestureHandlerRegistry { } @Synchronized - private fun detachHandler(handler: GestureHandler) { + private fun detachHandlerInternal(handler: GestureHandler) { val attachedToView = attachedTo[handler.tag] if (attachedToView != null) { attachedTo.remove(handler.tag) @@ -71,10 +71,17 @@ class RNGestureHandlerRegistry : GestureHandlerRegistry { } } + @Synchronized + fun detachHandler(handlerTag: Int) { + handlers[handlerTag]?.let { + detachHandlerInternal(it) + } + } + @Synchronized fun dropHandler(handlerTag: Int) { handlers[handlerTag]?.let { - detachHandler(it) + detachHandlerInternal(it) handlers.remove(handlerTag) } } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h index a191817419..d1d1566086 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h @@ -27,6 +27,8 @@ - (void)updateGestureHandler:(nonnull NSNumber *)handlerTag config:(nonnull NSDictionary *)config; +- (void)detachGestureHandler:(nonnull NSNumber *)handlerTag; + - (void)dropGestureHandler:(nonnull NSNumber *)handlerTag; - (void)dropAllGestureHandlers; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 7c4821410a..e90dfa6a93 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -206,6 +206,11 @@ - (void)updateGestureHandler:(NSNumber *)handlerTag config:(NSDictionary *)confi [handler configure:config]; } +- (void)detachGestureHandler:(NSNumber *)handlerTag +{ + [_registry detachHandlerWithTag:handlerTag]; +} + - (void)dropGestureHandler:(NSNumber *)handlerTag { [_registry dropHandlerWithTag:handlerTag]; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm index 53a654967f..951fb5d2e6 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm @@ -177,6 +177,13 @@ - (void)updateGestureHandler:(double)handlerTag newConfig:(NSDictionary *)config }]; } +- (void)detachGestureHandler:(double)handlerTag +{ + [self addOperationBlock:^(RNGestureHandlerManager *manager) { + [manager detachGestureHandler:[NSNumber numberWithDouble:handlerTag]]; + }]; +} + - (void)dropGestureHandler:(double)handlerTag { [self addOperationBlock:^(RNGestureHandlerManager *manager) { diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h index 5e86c07973..dd73a1ce89 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h @@ -15,6 +15,7 @@ - (void)attachHandlerWithTag:(nonnull NSNumber *)handlerTag toView:(nonnull RNGHUIView *)view withActionType:(RNGestureHandlerActionType)actionType; +- (void)detachHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropAllHandlers; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m index 197e0409d6..856c568e53 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m @@ -43,6 +43,12 @@ - (void)attachHandlerWithTag:(NSNumber *)handlerTag [handler bindToView:view]; } +- (void)detachHandlerWithTag:(NSNumber *)handlerTag +{ + RNGestureHandler *handler = _handlers[handlerTag]; + [handler unbindFromView]; +} + - (void)dropHandlerWithTag:(NSNumber *)handlerTag { RNGestureHandler *handler = _handlers[handlerTag]; diff --git a/packages/react-native-gesture-handler/src/specs/NativeRNGestureHandlerModule.ts b/packages/react-native-gesture-handler/src/specs/NativeRNGestureHandlerModule.ts index da74171519..7a4e2ce4d2 100644 --- a/packages/react-native-gesture-handler/src/specs/NativeRNGestureHandlerModule.ts +++ b/packages/react-native-gesture-handler/src/specs/NativeRNGestureHandlerModule.ts @@ -18,6 +18,8 @@ export interface Spec extends TurboModule { newView: Double, actionType: Double ) => void; + // TODO: implement on web (and mac?) + detachGestureHandler: (handlerTag: Double) => void; // eslint-disable-next-line @typescript-eslint/ban-types updateGestureHandler: (handlerTag: Double, newConfig: Object) => void; dropGestureHandler: (handlerTag: Double) => void; From a059f18bbc3c9483ee922ab3bd954914b830579e Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Sat, 22 Feb 2025 13:40:46 +0100 Subject: [PATCH 08/45] POC gesture lifecycle --- apps/basic-example/src/App.tsx | 45 +++++++++++++--- .../apple/RNGestureHandlerActionType.h | 1 + .../apple/RNGestureHandlerDetector.mm | 51 +++++++++++++++++-- .../apple/RNGestureHandlerManager.h | 3 ++ .../apple/RNGestureHandlerManager.mm | 6 ++- .../apple/RNGestureHandlerModule.h | 4 ++ .../apple/RNGestureHandlerModule.mm | 13 +++-- .../src/NativeDetector.tsx | 9 ++-- .../react-native-gesture-handler/src/index.ts | 3 ++ .../src/useGesture.ts | 45 ++++++++++++++++ 10 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 packages/react-native-gesture-handler/src/useGesture.ts diff --git a/apps/basic-example/src/App.tsx b/apps/basic-example/src/App.tsx index 8f5193ee0c..977d99b37b 100644 --- a/apps/basic-example/src/App.tsx +++ b/apps/basic-example/src/App.tsx @@ -1,6 +1,12 @@ +/* eslint-disable react-native/no-inline-styles */ import * as React from 'react'; -import { SafeAreaView, Platform } from 'react-native'; -import { GestureHandlerRootView } from 'react-native-gesture-handler'; +import { Button, SafeAreaView, View } from 'react-native'; +import { + GestureHandlerRootView, + NativeDetector, + RawButton, + useGesture, +} from 'react-native-gesture-handler'; import Navigator from './Navigator'; @@ -48,12 +54,37 @@ Stack.setRoutes({ }); export default function App() { + const [visible, setVisible] = React.useState(true); + const tap = useGesture('TapGestureHandler', { + numberOfTaps: 2, + }); + return ( - - - - + +