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
This commit is contained in:
Jorge Cabiedes Acosta
2025-04-22 13:17:21 -07:00
committed by Facebook GitHub Bot
parent c31699b760
commit 98c10d568e
3 changed files with 41 additions and 0 deletions
@@ -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)
@@ -253,6 +253,40 @@ jint FabricUIManagerBinding::findNextFocusableElement(
return nextNode->getTag();
}
jint FabricUIManagerBinding::findRelativeTopMostParent(
jint rootTag,
jint childTag) {
std::shared_ptr<UIManager> 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),
});
}
@@ -135,6 +135,8 @@ class FabricUIManagerBinding : public jni::HybridClass<FabricUIManagerBinding>,
jint
findNextFocusableElement(jint parentTag, jint focusedTag, jint direction);
jint findRelativeTopMostParent(jint rootTag, jint childTag);
void uninstallFabricUIManager();
// Private member variables