From fc7385c1ea50aec0f56e5436944c5c4637d9db83 Mon Sep 17 00:00:00 2001 From: Jorge Cabiedes Acosta Date: Fri, 14 Mar 2025 15:15:01 -0700 Subject: [PATCH] Back out "Fix keyboard navigation for FlatList with `removeClippedSubviews` enabled" Summary: Original commit changeset: 1df7f90066df Original Phabricator Diff: D69618406 Original diff caused unintended scrolling on some interactions that trigger focus searching Changelog: [Internal] Reviewed By: Abbondanzo Differential Revision: D71217683 fbshipit-source-id: faa977ba0fddbf776868d6204d283555e169ba96 --- .../ReactAndroid/api/ReactAndroid.api | 12 -- .../react/fabric/FabricUIManager.java | 46 ------ .../react/fabric/FabricUIManagerBinding.kt | 6 - .../react/fabric/NextFocusableNode.kt | 10 -- .../scroll/ReactHorizontalScrollView.java | 13 -- .../react/views/scroll/ReactScrollView.java | 14 -- .../views/scroll/ReactScrollViewHelper.kt | 87 ----------- .../react/fabric/FabricUIManagerBinding.cpp | 113 -------------- .../jni/react/fabric/FabricUIManagerBinding.h | 5 - .../jni/react/fabric/FocusOrderingHelper.cpp | 139 ------------------ .../jni/react/fabric/FocusOrderingHelper.h | 42 ------ 11 files changed, 487 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/NextFocusableNode.kt 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/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index acf780705b3..194c061635d 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2396,7 +2396,6 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid public fun dispatchCommand (IILcom/facebook/react/bridge/ReadableArray;)V public fun dispatchCommand (IILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun dispatchCommand (ILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V - public fun findNextFocusableElementMetrics (III)Lcom/facebook/react/fabric/NextFocusableNode; public fun getColor (I[Ljava/lang/String;)I public fun getEventDispatcher ()Lcom/facebook/react/uimanager/events/EventDispatcher; public fun getPerformanceCounters ()Ljava/util/Map; @@ -2436,12 +2435,6 @@ public final class com/facebook/react/fabric/FabricUIManagerProviderImpl : com/f public fun createUIManager (Lcom/facebook/react/bridge/ReactApplicationContext;)Lcom/facebook/react/bridge/UIManager; } -public final class com/facebook/react/fabric/NextFocusableNode { - public fun (IF)V - public final fun getDeltaScroll ()F - public final fun getId ()I -} - public class com/facebook/react/fabric/StateWrapperImpl : com/facebook/jni/HybridClassBase, com/facebook/react/uimanager/StateWrapper { public fun destroyState ()V public fun getStateData ()Lcom/facebook/react/bridge/ReadableNativeMap; @@ -6204,7 +6197,6 @@ public class com/facebook/react/views/scroll/ReactHorizontalScrollView : android public fun executeKeyEvent (Landroid/view/KeyEvent;)Z public fun flashScrollIndicators ()V public fun fling (I)V - public fun focusSearch (Landroid/view/View;I)Landroid/view/View; public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z public fun getClippingRect (Landroid/graphics/Rect;)V public fun getFlingAnimator ()Landroid/animation/ValueAnimator; @@ -6327,7 +6319,6 @@ public class com/facebook/react/views/scroll/ReactScrollView : android/widget/Sc public fun executeKeyEvent (Landroid/view/KeyEvent;)Z public fun flashScrollIndicators ()V public fun fling (I)V - public fun focusSearch (Landroid/view/View;I)Landroid/view/View; public fun getChildVisibleRect (Landroid/view/View;Landroid/graphics/Rect;Landroid/graphics/Point;)Z public fun getClippingRect (Landroid/graphics/Rect;)V public fun getFlingAnimator ()Landroid/animation/ValueAnimator; @@ -6448,8 +6439,6 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper { public static final fun emitScrollEvent (Landroid/view/ViewGroup;FF)V public static final fun emitScrollMomentumBeginEvent (Landroid/view/ViewGroup;II)V public static final fun emitScrollMomentumEndEvent (Landroid/view/ViewGroup;)V - public static final fun findNextClippedElement (Landroid/view/ViewGroup;Landroid/view/View;ILcom/facebook/react/bridge/ReactContext;)Landroid/view/View; - public static final fun findNextFocusableView (Landroid/view/ViewGroup;Landroid/view/View;IZ)Landroid/view/View; public static final fun forceUpdateState (Landroid/view/ViewGroup;)V public static final fun getDefaultScrollAnimationDuration (Landroid/content/Context;)I public static final fun getNextFlingStartValue (Landroid/view/ViewGroup;III)I @@ -6459,7 +6448,6 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper { public final fun registerFlingAnimator (Landroid/view/ViewGroup;)V public static final fun removeLayoutChangeListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$LayoutChangeListener;)V public static final fun removeScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V - public static final fun resolveAbsoluteDirection (IZI)I public static final fun smoothScrollTo (Landroid/view/ViewGroup;II)V public static final fun updateFabricScrollState (Landroid/view/ViewGroup;)V public final fun updateFabricScrollState (Landroid/view/ViewGroup;II)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 0fcdb771091..0ecb1ace9f2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -29,7 +29,6 @@ import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import androidx.core.view.ViewCompat.FocusRealDirection; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.ThreadConfined; import com.facebook.proguard.annotations.DoNotStripAny; @@ -261,51 +260,6 @@ public class FabricUIManager return rootTag; } - /** - * Find the next focusable element's id and position relative to the parent from the shadow tree - * based on the current focusable element and the direction. - * - * @return A NextFocusableNode object where the 'id' is the reactId/Tag of the next focusable - * view, and 'deltaScroll' is the scroll delta needed to make the view visible on the screen. - * Returns null if no valid node is found. - */ - public @Nullable NextFocusableNode findNextFocusableElementMetrics( - int parentTag, int focusedTag, @FocusRealDirection int direction) { - if (mBinding == null) { - return null; - } - - int generalizedDirection; - - switch (direction) { - case View.FOCUS_DOWN: - generalizedDirection = 0; - break; - case View.FOCUS_UP: - generalizedDirection = 1; - break; - case View.FOCUS_RIGHT: - generalizedDirection = 2; - break; - case View.FOCUS_LEFT: - generalizedDirection = 3; - break; - default: - return null; - } - - @Nullable - float[] serializedNextFocusableNodeMetrics = - mBinding.findNextFocusableElementMetrics(parentTag, focusedTag, generalizedDirection); - - if (serializedNextFocusableNodeMetrics == null) { - return null; - } - - return new NextFocusableNode( - (int) serializedNextFocusableNodeMetrics[0], serializedNextFocusableNodeMetrics[1]); - } - @Override @AnyThread @ThreadConfined(ANY) 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 6c8b64c2b84..13c448525a2 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,12 +55,6 @@ internal class FabricUIManagerBinding : HybridClassBase() { isMountable: Boolean ) - external fun findNextFocusableElementMetrics( - parentTag: Int, - focusedTag: Int, - direction: Int - ): FloatArray - external fun stopSurface(surfaceId: Int) external fun stopSurfaceWithSurfaceHandler(surfaceHandler: SurfaceHandlerBinding) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/NextFocusableNode.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/NextFocusableNode.kt deleted file mode 100644 index 1b2e88e813a..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/NextFocusableNode.kt +++ /dev/null @@ -1,10 +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. - */ - -package com.facebook.react.fabric - -public class NextFocusableNode(public val id: Int, public val deltaScroll: Float) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index e326fcfed81..e037f05ad19 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -11,7 +11,6 @@ import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNME import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED; import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END; import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START; -import static com.facebook.react.views.scroll.ReactScrollViewHelper.findNextFocusableView; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -32,7 +31,6 @@ import android.widget.HorizontalScrollView; import android.widget.OverScroller; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; -import androidx.core.view.ViewCompat.FocusRealDirection; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Nullsafe; @@ -774,17 +772,6 @@ public class ReactHorizontalScrollView extends HorizontalScrollView } } - @Override - public @Nullable View focusSearch(View focused, @FocusRealDirection int direction) { - @Nullable View nextfocusableView = findNextFocusableView(this, focused, direction, true); - - if (nextfocusableView != null) { - return nextfocusableView; - } - - return super.focusSearch(focused, direction); - } - @Override public void updateClippingRect() { if (!mRemoveClippedSubviews) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java index 0b321c07dd1..71acfa6288b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java @@ -11,7 +11,6 @@ import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNME import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_DISABLED; import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_END; import static com.facebook.react.views.scroll.ReactScrollViewHelper.SNAP_ALIGNMENT_START; -import static com.facebook.react.views.scroll.ReactScrollViewHelper.findNextFocusableView; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -32,7 +31,6 @@ import android.widget.ScrollView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; -import androidx.core.view.ViewCompat.FocusRealDirection; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Nullsafe; @@ -361,18 +359,6 @@ public class ReactScrollView extends ScrollView } } - @Override - public @Nullable View focusSearch(View focused, @FocusRealDirection int direction) { - - @Nullable View nextfocusableView = findNextFocusableView(this, focused, direction, false); - - if (nextfocusableView != null) { - return nextfocusableView; - } - - return super.focusSearch(focused, direction); - } - /** * Since ReactScrollView handles layout changes on JS side, it does not call super.onlayout due to * which mIsLayoutDirty flag in ScrollView remains true and prevents scrolling to child when diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt index 517dddb4905..5b3f661c634 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.kt @@ -11,19 +11,15 @@ import android.animation.Animator import android.animation.ValueAnimator import android.content.Context import android.graphics.Point -import android.view.FocusFinder import android.view.View import android.view.ViewGroup import android.widget.OverScroller -import androidx.core.view.ViewCompat.FocusRealDirection import com.facebook.common.logging.FLog import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap import com.facebook.react.common.ReactConstants -import com.facebook.react.fabric.FabricUIManager import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel -import com.facebook.react.uimanager.PixelUtil.toPixelFromDIP import com.facebook.react.uimanager.StateWrapper import com.facebook.react.uimanager.UIManagerHelper import com.facebook.react.uimanager.common.UIManagerType @@ -466,89 +462,6 @@ public object ReactScrollViewHelper { return Point(scroller.finalX, scroller.finalY) } - @JvmStatic - public fun findNextFocusableView( - host: ViewGroup, - focused: View, - @FocusRealDirection direction: Int, - horizontal: Boolean - ): View? { - val absDir = resolveAbsoluteDirection(direction, horizontal, host.getLayoutDirection()) - - /* - * Check if we can focus the next element in the absolute direction within the ScrollView, - * if we can't, look into the shadow tree to find the next focusable element - */ - val ff = FocusFinder.getInstance() - val result = ff.findNextFocus(host, focused, absDir) - - if (result != null) { - return result - } - - /* - * Attempt to focus the next focusable but clipped element on the list if there is one, since - * the view is clipped it is not currently in the hierarchy so we scroll it into view and then - * focus it. - */ - return findNextClippedElement(host, focused, absDir, host.context as ReactContext) - } - - /** - * Attempts to focus the next element in the specified direction within the scrollView. - * - * @return true if a new element was successfully focused, otherwise false. - */ - @JvmStatic - public fun findNextClippedElement( - scrollView: ViewGroup, - focused: View, - @FocusRealDirection direction: Int, - context: ReactContext, - ): View? { - val uimanager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC) ?: return null - - val nextFocusableViewMetrics = - (uimanager as FabricUIManager).findNextFocusableElementMetrics( - scrollView.id, focused.id, direction) - - if (nextFocusableViewMetrics != null) { - - when (direction) { - View.FOCUS_UP, - View.FOCUS_DOWN -> { - scrollView.scrollBy(0, toPixelFromDIP(nextFocusableViewMetrics.deltaScroll).toInt()) - } - View.FOCUS_RIGHT, - View.FOCUS_LEFT -> { - scrollView.scrollBy(toPixelFromDIP(nextFocusableViewMetrics.deltaScroll).toInt(), 0) - } - else -> return null - } - return scrollView.findViewById(nextFocusableViewMetrics.id) - } - return null - } - - @JvmStatic - public fun resolveAbsoluteDirection( - @FocusRealDirection direction: Int, - horizontal: Boolean, - layoutDirection: Int - ): Int { - val rtl: Boolean = layoutDirection == View.LAYOUT_DIRECTION_RTL - - return if (direction == View.FOCUS_FORWARD || direction == View.FOCUS_BACKWARD) { - if (horizontal) { - if ((direction == View.FOCUS_FORWARD) != rtl) View.FOCUS_RIGHT else View.FOCUS_LEFT - } else { - if (direction == View.FOCUS_FORWARD) View.FOCUS_DOWN else View.FOCUS_UP - } - } else { - direction - } - } - public interface ScrollListener { public fun onScroll( scrollView: ViewGroup?, 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 f7a13acc179..4b6418c5500 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 @@ -12,7 +12,6 @@ #include "EventBeatManager.h" #include "EventEmitterWrapper.h" #include "FabricMountingManager.h" -#include "FocusOrderingHelper.h" #include #include @@ -196,115 +195,6 @@ void FabricUIManagerBinding::startSurface( } } -jfloatArray FabricUIManagerBinding::findNextFocusableElementMetrics( - jint parentTag, - jint focusedTag, - jint direction) { - /* - * Holds the values of the next focusable element - * [0] - tag - * [1] - delta - */ - std::array nodeValues{-1, -1}; - - JNIEnv* env = jni::Environment::current(); - - ShadowNode::Shared nextNode; - - auto nodeValuesSize = static_cast(nodeValues.size()); - jfloatArray result = env->NewFloatArray(nodeValuesSize); - - std::optional focusDirection = - FocusOrderingHelper::resolveFocusDirection(direction); - - if (!focusDirection.has_value()) { - return nullptr; - } - - ShadowNode::Shared parentShadowNode; - ShadowNode::Shared focusedShadowNode; - std::shared_ptr uimanager = getScheduler()->getUIManager(); - - parentShadowNode = uimanager->findShadowNodeByTag_DEPRECATED(parentTag); - - if (parentShadowNode == nullptr) { - return nullptr; - } - - focusedShadowNode = FocusOrderingHelper::findShadowNodeByTagRecursively( - parentShadowNode, focusedTag); - - if (focusedShadowNode == nullptr) { - return nullptr; - } - - LayoutMetrics childLayoutMetrics = uimanager->getRelativeLayoutMetrics( - *focusedShadowNode, parentShadowNode.get(), {.includeTransform = true}); - - /* - * Set currentDelta to the origin of the focused node - * Set nextDelta to the max int value in the direction we want to go (down = - * INT_MIN, up = INT_MAX) - */ - auto [currentDelta, nextDelta] = FocusOrderingHelper::initScrollDeltas( - focusDirection.value(), childLayoutMetrics.frame.origin); - - /* - * Traverse the tree recursively to find the next focusable element in the - * given direction - */ - FocusOrderingHelper::traverseAndUpdateNextFocusableElementMetrics( - parentShadowNode, - focusedShadowNode, - parentShadowNode, - focusDirection.value(), - *uimanager, - currentDelta, - nextDelta, - nextNode); - - if (nextNode == nullptr) { - return nullptr; - } - - LayoutMetrics nextNodeLayoutMetrics = uimanager->getRelativeLayoutMetrics( - *nextNode, parentShadowNode.get(), {.includeTransform = true}); - - LayoutMetrics parentNodeLayoutMetrics = uimanager->getRelativeLayoutMetrics( - *parentShadowNode, nullptr, {.includeTransform = true}); - - nodeValues[0] = static_cast(nextNode->getTag()); - - /* - * In the end we want to scroll enough to make the next node visible - * When going up or right we want delta to be the origin of the next node - * When going down or left we want delta to be the end of the next node - */ - switch (focusDirection.value()) { - case FocusDirection::FocusDown: - nextDelta = (nextNodeLayoutMetrics.frame.origin.y - - parentNodeLayoutMetrics.frame.size.height) + - nextNodeLayoutMetrics.frame.size.height; - nodeValues[1] = nextDelta; - break; - case FocusDirection::FocusRight: - nextDelta = (nextNodeLayoutMetrics.frame.origin.x - - parentNodeLayoutMetrics.frame.size.width) + - nextNodeLayoutMetrics.frame.size.width; - nodeValues[1] = nextDelta; - break; - case FocusDirection::FocusLeft: - case FocusDirection::FocusUp: - nodeValues[1] = nextDelta; - break; - } - - // Build the jfloatArray with the updates nodeValues - env->SetFloatArrayRegion(result, 0, nodeValuesSize, nodeValues.data()); - - return result; -} - // Used by non-bridgeless+Fabric void FabricUIManagerBinding::startSurfaceWithConstraints( jint surfaceId, @@ -766,9 +656,6 @@ void FabricUIManagerBinding::registerNatives() { makeNativeMethod( "stopSurfaceWithSurfaceHandler", FabricUIManagerBinding::stopSurfaceWithSurfaceHandler), - makeNativeMethod( - "findNextFocusableElementMetrics", - FabricUIManagerBinding::findNextFocusableElementMetrics), }); } 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 7d3b2303837..50c234eca5f 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 @@ -128,11 +128,6 @@ class FabricUIManagerBinding : public jni::HybridClass, void reportMount(SurfaceId surfaceId); - jfloatArray findNextFocusableElementMetrics( - 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 51abc61c8f8..00000000000 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.cpp +++ /dev/null @@ -1,139 +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 - -namespace facebook::react { - -void FocusOrderingHelper::traverseAndUpdateNextFocusableElementMetrics( - const ShadowNode::Shared& parentShadowNode, - const ShadowNode::Shared& focusedShadowNode, - const ShadowNode::Shared& currNode, - FocusDirection focusDirection, - const UIManager& uimanager, - float& currentDelta, - float& nextDelta, - 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 (props != nullptr && props->focusable) { - LayoutMetrics nodeLayoutMetrics = uimanager.getRelativeLayoutMetrics( - *currNode, parentShadowNode.get(), {.includeTransform = true}); - - switch (focusDirection) { - case FocusDirection::FocusDown: - if (nodeLayoutMetrics.frame.origin.y > currentDelta && - nodeLayoutMetrics.frame.origin.y < nextDelta && - currNode != focusedShadowNode) { - nextNode = currNode; - nextDelta = nodeLayoutMetrics.frame.origin.y; - } - break; - case FocusDirection::FocusUp: - if (nodeLayoutMetrics.frame.origin.y < currentDelta && - nodeLayoutMetrics.frame.origin.y > nextDelta && - currNode != focusedShadowNode) { - nextNode = currNode; - nextDelta = nodeLayoutMetrics.frame.origin.y; - } - break; - case FocusDirection::FocusRight: - if (nodeLayoutMetrics.frame.origin.x > currentDelta && - nodeLayoutMetrics.frame.origin.x < nextDelta && - currNode != focusedShadowNode) { - nextNode = currNode; - nextDelta = nodeLayoutMetrics.frame.origin.x; - } - break; - case FocusDirection::FocusLeft: - if (nodeLayoutMetrics.frame.origin.x < currentDelta && - nodeLayoutMetrics.frame.origin.x > nextDelta && - currNode != focusedShadowNode) { - nextNode = currNode; - nextDelta = nodeLayoutMetrics.frame.origin.x; - } - break; - } - } - - for (auto& child : currNode->getChildren()) { - if (child->getTraits().check(ShadowNodeTraits::Trait::RootNodeKind)) { - continue; - } - - traverseAndUpdateNextFocusableElementMetrics( - parentShadowNode, - focusedShadowNode, - child, - focusDirection, - uimanager, - currentDelta, - nextDelta, - 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; -} - -std::tuple FocusOrderingHelper::initScrollDeltas( - FocusDirection focusDirection, - Point refPoint) { - float currentDelta = 0; - float nextDelta = 0; - - switch (focusDirection) { - case FocusDirection::FocusDown: - currentDelta = refPoint.y; - nextDelta = std::numeric_limits::max(); - break; - case FocusDirection::FocusUp: - currentDelta = refPoint.y; - nextDelta = -std::numeric_limits::max(); - break; - case FocusDirection::FocusRight: - currentDelta = refPoint.x; - nextDelta = std::numeric_limits::max(); - break; - case FocusDirection::FocusLeft: - currentDelta = refPoint.x; - nextDelta = -std::numeric_limits::max(); - break; - } - return std::make_tuple(currentDelta, nextDelta); -} -} // 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 65167d54ce8..00000000000 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FocusOrderingHelper.h +++ /dev/null @@ -1,42 +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 traverseAndUpdateNextFocusableElementMetrics( - const ShadowNode::Shared& parentShadowNode, - const ShadowNode::Shared& focusedShadowNode, - const ShadowNode::Shared& currNode, - FocusDirection focusDirection, - const UIManager& uimanager, - float& currentDelta, - float& nextDelta, - ShadowNode::Shared& nextNode); - - static ShadowNode::Shared findShadowNodeByTagRecursively( - const ShadowNode::Shared& parentShadowNode, - Tag tag); - - static std::optional resolveFocusDirection(int direction); - - static std::tuple initScrollDeltas( - FocusDirection focusDirection, - Point refPoint); -}; -} // namespace facebook::react