From 98c10d568e9f5828dc14c104fccaa0174fedeb30 Mon Sep 17 00:00:00 2001 From: Jorge Cabiedes Acosta Date: Tue, 22 Apr 2025 13:17:21 -0700 Subject: [PATCH] Add fabric implementation to find Top-Most relative and relevant parent of a child view (#50825) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50825 Pull Request resolved: https://github.com/facebook/react-native/pull/50404 Add another function to fabric to get the topmost stacking context parent given a root and a child. This is to be used on focus searching algorithm in the case where the next focusable child is deeper in the hierarchy meaning we need to find the top most parent in the Android hierarchy and lay that out as well before transferring focus. If we don't lay out the parent as well as the next focusable view: - The next focusable view might lack context given by the parent - If the parent is a scrollview and has removeClippedSubviews enabled then laying out the next focusable view will not work - If the view is deeper in the android hierarchy in some cases it won't be layed out unless the parent is Changelog: [Internal] Reviewed By: NickGerleman Differential Revision: D73114933 fbshipit-source-id: 081720199943eff78966982a5fd1c921d4e105fd --- .../react/fabric/FabricUIManagerBinding.kt | 2 + .../react/fabric/FabricUIManagerBinding.cpp | 37 +++++++++++++++++++ .../jni/react/fabric/FabricUIManagerBinding.h | 2 + 3 files changed, 41 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt index 06ee1e4f503..f76c676af87 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt @@ -57,6 +57,8 @@ internal class FabricUIManagerBinding : HybridClassBase() { external fun findNextFocusableElement(parentTag: Int, focusedTag: Int, direction: Int): Int + external fun findRelativeTopMostParent(rootTag: Int, childTag: Int): Int + external fun stopSurface(surfaceId: Int) external fun stopSurfaceWithSurfaceHandler(surfaceHandler: SurfaceHandlerBinding) diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp index a128dac13bd..50f20e19aa3 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp @@ -253,6 +253,40 @@ jint FabricUIManagerBinding::findNextFocusableElement( return nextNode->getTag(); } +jint FabricUIManagerBinding::findRelativeTopMostParent( + jint rootTag, + jint childTag) { + std::shared_ptr uimanager = getScheduler()->getUIManager(); + + ShadowNode::Shared childShadowNode = + uimanager->findShadowNodeByTag_DEPRECATED(childTag); + ShadowNode::Shared rootShadowNode = + uimanager->findShadowNodeByTag_DEPRECATED(rootTag); + + if (childShadowNode == nullptr || rootShadowNode == nullptr) { + return -1; + } + + ShadowNode::AncestorList ancestorList = + childShadowNode->getFamily().getAncestors(*rootShadowNode); + + if (ancestorList.empty() || ancestorList.size() < 2) { + return -1; + } + + // ignore the first ancestor as it is the rootShadowNode itself + for (auto it = std::next(ancestorList.begin()); it != ancestorList.end(); + ++it) { + auto& ancestor = *it; + if (ancestor.first.get().getTraits().check( + ShadowNodeTraits::Trait::FormsStackingContext)) { + return ancestor.first.get().getTag(); + } + } + + return -1; +} + // Used by non-bridgeless+Fabric void FabricUIManagerBinding::startSurfaceWithConstraints( jint surfaceId, @@ -728,6 +762,9 @@ void FabricUIManagerBinding::registerNatives() { makeNativeMethod( "findNextFocusableElement", FabricUIManagerBinding::findNextFocusableElement), + makeNativeMethod( + "findRelativeTopMostParent", + FabricUIManagerBinding::findRelativeTopMostParent), }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h index fd298701562..e045425269d 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h @@ -135,6 +135,8 @@ class FabricUIManagerBinding : public jni::HybridClass, jint findNextFocusableElement(jint parentTag, jint focusedTag, jint direction); + jint findRelativeTopMostParent(jint rootTag, jint childTag); + void uninstallFabricUIManager(); // Private member variables