From eee1d8b11df98d1c830dd0ef78e855c76c97ffaf Mon Sep 17 00:00:00 2001 From: Gijs Weterings Date: Wed, 16 Apr 2025 07:28:41 -0700 Subject: [PATCH] Back out "Add fabric implementation to find next focusable view" Summary: Original commit changeset: 1a13c82d0678 Original Phabricator Diff: D71558965 Changelog: [Internal] Differential Revision: D73103165 fbshipit-source-id: b7813a03224dad550ff6d3d99c2b308df5cb4a55 --- .../react/fabric/FabricUIManagerBinding.kt | 2 - .../react/fabric/FabricUIManagerBinding.cpp | 52 ----- .../jni/react/fabric/FabricUIManagerBinding.h | 3 - .../jni/react/fabric/FocusOrderingHelper.cpp | 186 ------------------ .../jni/react/fabric/FocusOrderingHelper.h | 38 ---- 5 files changed, 281 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.cpp delete mode 100644 packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.h 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..7b9f9ee4500 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 @@ -55,8 +55,6 @@ internal class FabricUIManagerBinding : HybridClassBase() { isMountable: Boolean ) - external fun findNextFocusableElement(parentTag: Int, focusedTag: Int, direction: 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 5473ceb47d5..e3224ffac39 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 @@ -11,7 +11,6 @@ #include "ComponentFactory.h" #include "EventBeatManager.h" #include "FabricMountingManager.h" -#include "FocusOrderingHelper.h" #include #include @@ -196,54 +195,6 @@ void FabricUIManagerBinding::startSurface( } } -jint FabricUIManagerBinding::findNextFocusableElement( - jint parentTag, - jint focusedTag, - jint direction) { - ShadowNode::Shared nextNode; - - std::optional focusDirection = - FocusOrderingHelper::resolveFocusDirection(direction); - - if (!focusDirection.has_value()) { - return -1; - } - - std::shared_ptr uimanager = getScheduler()->getUIManager(); - - ShadowNode::Shared parentShadowNode = - uimanager->findShadowNodeByTag_DEPRECATED(parentTag); - ShadowNode::Shared focusedShadowNode = - FocusOrderingHelper::findShadowNodeByTagRecursively( - parentShadowNode, focusedTag); - - LayoutMetrics childLayoutMetrics = uimanager->getRelativeLayoutMetrics( - *focusedShadowNode, parentShadowNode.get(), {.includeTransform = true}); - - Rect sourceRect = childLayoutMetrics.frame; - - /* - * Traverse the tree recursively to find the next focusable element in the - * given direction - */ - std::optional nextRect = std::nullopt; - FocusOrderingHelper::traverseAndUpdateNextFocusableElement( - parentShadowNode, - focusedShadowNode, - parentShadowNode, - focusDirection.value(), - *uimanager, - sourceRect, - nextRect, - nextNode); - - if (nextNode == nullptr) { - return -1; - } - - return nextNode->getTag(); -} - // Used by non-bridgeless+Fabric void FabricUIManagerBinding::startSurfaceWithConstraints( jint surfaceId, @@ -716,9 +667,6 @@ void FabricUIManagerBinding::registerNatives() { makeNativeMethod( "stopSurfaceWithSurfaceHandler", FabricUIManagerBinding::stopSurfaceWithSurfaceHandler), - makeNativeMethod( - "findNextFocusableElement", - FabricUIManagerBinding::findNextFocusableElement), }); } 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..c3c404c9990 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 @@ -132,9 +132,6 @@ class FabricUIManagerBinding : public jni::HybridClass, void reportMount(SurfaceId surfaceId); - jint - findNextFocusableElement(jint parentTag, jint focusedTag, jint direction); - void uninstallFabricUIManager(); // Private member variables diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.cpp deleted file mode 100644 index e30b1e2c89a..00000000000 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include "FocusOrderingHelper.h" -#include -#include - -namespace facebook::react { - -int majorAxisDistanceRaw( - FocusDirection focusDirection, - Rect source, - Rect dest) { - switch (focusDirection) { - case FocusDirection::FocusLeft: - return static_cast( - source.origin.x - (dest.origin.x + dest.size.width)); - case FocusDirection::FocusRight: - return static_cast( - dest.origin.x - (source.origin.x + source.size.width)); - case FocusDirection::FocusUp: - return static_cast( - source.origin.y - (dest.origin.y + dest.size.height)); - case FocusDirection::FocusDown: - return static_cast( - dest.origin.y - (source.origin.y + source.size.height)); - } -} - -int majorAxisDistance(FocusDirection focusDirection, Rect source, Rect dest) { - return std::max(0, majorAxisDistanceRaw(focusDirection, source, dest)); -} - -int minorAxisDistance(FocusDirection direction, Rect source, Rect dest) { - switch (direction) { - case FocusDirection::FocusLeft: - case FocusDirection::FocusRight: - // the distance between the center verticals - return static_cast(abs((source.getMidY() - dest.getMidY()))); - case FocusDirection::FocusUp: - case FocusDirection::FocusDown: - // the distance between the center horizontals - return static_cast(abs((source.getMidX() - dest.getMidX()))); - } -} - -// 13 is a magic number that comes from Android's implementation. We opt to use -// this to get the same focus ordering as Android. See: -// https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/FocusFinder.java;l=547 -int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) { - return 13 * majorAxisDistance * majorAxisDistance + - minorAxisDistance * minorAxisDistance; -} - -// Make sure dest rect is actually on the direction of focus -bool isCandidate(Rect source, Rect dest, FocusDirection focusDirection) { - switch (focusDirection) { - case FocusDirection::FocusLeft: - return ((source.origin.x + source.size.width) > - (dest.origin.x + dest.size.width) || - source.origin.x >= (dest.origin.x + dest.size.width)) && - source.origin.x > dest.origin.x; - case FocusDirection::FocusRight: - return (source.origin.x < dest.origin.x || - (source.origin.x + source.size.width) <= dest.origin.x) && - (source.origin.x + source.size.width) < - (dest.origin.x + dest.size.width); - case FocusDirection::FocusUp: - return ((source.origin.y + source.size.height) > - (dest.origin.y + dest.size.height) || - source.origin.y >= (dest.origin.y + dest.size.height)) && - source.origin.y > dest.origin.y; - case FocusDirection::FocusDown: - return (source.origin.y < dest.origin.y || - (source.origin.y + source.size.height) <= dest.origin.y) && - ((source.origin.y + source.size.height) < - (dest.origin.y + dest.size.height)); - } -} - -bool isBetterCandidate( - FocusDirection focusDirection, - Rect source, - Rect current, - Rect candidate) { - if (!isCandidate(source, candidate, focusDirection)) { - return false; - } - - int candidateWeightedDistance = getWeightedDistanceFor( - majorAxisDistance(focusDirection, source, candidate), - minorAxisDistance(focusDirection, source, candidate)); - - int currentWeightedDistance = getWeightedDistanceFor( - majorAxisDistance(focusDirection, source, current), - minorAxisDistance(focusDirection, source, current)); - - return candidateWeightedDistance < currentWeightedDistance; -} - -void FocusOrderingHelper::traverseAndUpdateNextFocusableElement( - const ShadowNode::Shared& parentShadowNode, - const ShadowNode::Shared& focusedShadowNode, - const ShadowNode::Shared& currNode, - FocusDirection focusDirection, - const UIManager& uimanager, - Rect sourceRect, - std::optional& nextRect, - ShadowNode::Shared& nextNode) { - const auto* props = - dynamic_cast(currNode->getProps().get()); - - // We only care about focusable elements since only they can be both - // focused and present in the hierarchy - if (currNode->getTraits().check(ShadowNodeTraits::Trait::KeyboardFocusable) || - (props != nullptr && - (props->focusable || props->accessible || props->hasTVPreferredFocus))) { - LayoutMetrics nodeLayoutMetrics = uimanager.getRelativeLayoutMetrics( - *currNode, parentShadowNode.get(), {.includeTransform = true}); - - if (nextRect == std::nullopt && - isCandidate(sourceRect, nodeLayoutMetrics.frame, focusDirection)) { - nextNode = currNode; - nextRect = nodeLayoutMetrics.frame; - } else if ( - nextRect != std::nullopt && - isBetterCandidate( - focusDirection, - sourceRect, - nextRect.value(), - nodeLayoutMetrics.frame)) { - nextNode = currNode; - nextRect = nodeLayoutMetrics.frame; - } - } - - for (auto& child : currNode->getChildren()) { - if (child->getTraits().check(ShadowNodeTraits::Trait::RootNodeKind)) { - continue; - } - - traverseAndUpdateNextFocusableElement( - parentShadowNode, - focusedShadowNode, - child, - focusDirection, - uimanager, - sourceRect, - nextRect, - nextNode); - }; -}; - -ShadowNode::Shared FocusOrderingHelper::findShadowNodeByTagRecursively( - const ShadowNode::Shared& parentShadowNode, - Tag tag) { - if (parentShadowNode->getTag() == tag) { - return parentShadowNode; - } - - for (auto& shadowNode : parentShadowNode->getChildren()) { - if (auto result = findShadowNodeByTagRecursively(shadowNode, tag)) { - return result; - } - } - - return nullptr; -} - -std::optional FocusOrderingHelper::resolveFocusDirection( - int direction) { - switch (static_cast(direction)) { - case FocusDirection::FocusDown: - case FocusDirection::FocusUp: - case FocusDirection::FocusRight: - case FocusDirection::FocusLeft: - return static_cast(direction); - } - - return std::nullopt; -} -} // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.h deleted file mode 100644 index 97eeba7d1ea..00000000000 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include "FabricUIManagerBinding.h" - -namespace facebook::react { - -enum class FocusDirection { - FocusDown = 0, - FocusUp = 1, - FocusRight = 2, - FocusLeft = 3, -}; - -class FocusOrderingHelper { - public: - static void traverseAndUpdateNextFocusableElement( - const ShadowNode::Shared& parentShadowNode, - const ShadowNode::Shared& focusedShadowNode, - const ShadowNode::Shared& currNode, - FocusDirection focusDirection, - const UIManager& uimanager, - Rect sourceRect, - std::optional& nextRect, - ShadowNode::Shared& nextNode); - - static ShadowNode::Shared findShadowNodeByTagRecursively( - const ShadowNode::Shared& parentShadowNode, - Tag tag); - - static std::optional resolveFocusDirection(int direction); -}; -} // namespace facebook::react