From 0a08ea54ac614629fc26d3f48df9492117ae2979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Fri, 18 Sep 2026 15:45:11 +0200 Subject: [PATCH 1/2] Make the detector frame cover transformed children --- .../RNGestureHandlerDetectorShadowNode.cpp | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp index 819a262acd..f435854829 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp @@ -11,6 +11,8 @@ #include "RNGestureHandlerDetectorShadowNode.h" +#include + #include namespace facebook::react { @@ -79,29 +81,36 @@ void RNGestureHandlerDetectorShadowNode::layout(LayoutContext layoutContext) { return; } - // Calculate bounding box of all children + // Calculate bounding box of all children. Float minX = std::numeric_limits::infinity(); Float minY = std::numeric_limits::infinity(); Float maxX = -std::numeric_limits::infinity(); Float maxY = -std::numeric_limits::infinity(); + const auto extend = [&](const Rect &rect) { + minX = std::min(minX, rect.getMinX()); + minY = std::min(minY, rect.getMinY()); + maxX = std::max(maxX, rect.getMaxX()); + maxY = std::max(maxY, rect.getMaxY()); + }; + for (const auto &child : children) { auto yogaChild = std::static_pointer_cast(child); const auto &frame = yogaChild->getLayoutMetrics().frame; - - minX = std::min(minX, frame.origin.x); - minY = std::min(minY, frame.origin.y); - maxX = std::max(maxX, frame.origin.x + frame.size.width); - maxY = std::max(maxY, frame.origin.y + frame.size.height); + extend(frame); + +#ifdef ANDROID + // Android's ViewGroup only dispatches touches inside a child's bounds, so + // the frame also covers the transformed content. iOS hit-tests outside the + // bounds through the overflowInset override in RNGestureHandlerDetector.mm. + const auto transform = yogaChild->getTransform(); + if (transform != Transform::Identity()) { + extend(frame * transform); + } +#endif } - // Set detector's metrics to the bounding box of all children - auto metrics = getLayoutMetrics(); - metrics.frame.origin = Point{minX, minY}; - metrics.frame.size = Size{maxX - minX, maxY - minY}; - setLayoutMetrics(metrics); - // Shift all children so their positions are relative to the detector's origin for (const auto &child : children) { auto yogaChild = @@ -115,6 +124,27 @@ void RNGestureHandlerDetectorShadowNode::layout(LayoutContext layoutContext) { childMetrics.frame.origin.y -= minY; mutableChild->setLayoutMetrics(childMetrics); } + + // Set detector's metrics to the bounding box of all children + auto metrics = getLayoutMetrics(); + metrics.frame.origin = Point{minX, minY}; + metrics.frame.size = Size{maxX - minX, maxY - minY}; + +#ifdef ANDROID + // The default layout computed the overflow inset against the zero-sized + // frame Yoga gives a `display: contents` node. Recompute it against the + // real frame; children are already shifted, so their bounds are relative. + const auto contentBounds = getContentBounds(); + metrics.overflowInset = EdgeInsets{ + .left = std::min(contentBounds.getMinX(), Float{0}), + .top = std::min(contentBounds.getMinY(), Float{0}), + .right = -std::max( + contentBounds.getMaxX() - metrics.frame.size.width, Float{0}), + .bottom = -std::max( + contentBounds.getMaxY() - metrics.frame.size.height, Float{0}), + }; +#endif + setLayoutMetrics(metrics); } std::shared_ptr From 7ab6e9a34541a0b3b24273ff18c838c2c7c01539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Mon, 21 Sep 2026 10:52:03 +0200 Subject: [PATCH 2/2] Remove dot --- .../RNGestureHandlerDetectorShadowNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp index f435854829..92bb180e5a 100644 --- a/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp +++ b/packages/react-native-gesture-handler/shared/shadowNodes/react/renderer/components/rngesturehandler_codegen/RNGestureHandlerDetectorShadowNode.cpp @@ -81,7 +81,7 @@ void RNGestureHandlerDetectorShadowNode::layout(LayoutContext layoutContext) { return; } - // Calculate bounding box of all children. + // Calculate bounding box of all children Float minX = std::numeric_limits::infinity(); Float minY = std::numeric_limits::infinity(); Float maxX = -std::numeric_limits::infinity();