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
This commit is contained in:
Gijs Weterings
2025-04-16 07:28:41 -07:00
committed by Facebook GitHub Bot
parent 1d5324cba9
commit eee1d8b11d
5 changed files with 0 additions and 281 deletions
@@ -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)
@@ -11,7 +11,6 @@
#include "ComponentFactory.h"
#include "EventBeatManager.h"
#include "FabricMountingManager.h"
#include "FocusOrderingHelper.h"
#include <cxxreact/TraceSection.h>
#include <fbjni/fbjni.h>
@@ -196,54 +195,6 @@ void FabricUIManagerBinding::startSurface(
}
}
jint FabricUIManagerBinding::findNextFocusableElement(
jint parentTag,
jint focusedTag,
jint direction) {
ShadowNode::Shared nextNode;
std::optional<FocusDirection> focusDirection =
FocusOrderingHelper::resolveFocusDirection(direction);
if (!focusDirection.has_value()) {
return -1;
}
std::shared_ptr<UIManager> 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<Rect> 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),
});
}
@@ -132,9 +132,6 @@ class FabricUIManagerBinding : public jni::HybridClass<FabricUIManagerBinding>,
void reportMount(SurfaceId surfaceId);
jint
findNextFocusableElement(jint parentTag, jint focusedTag, jint direction);
void uninstallFabricUIManager();
// Private member variables
@@ -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 <android/log.h>
#include <react/renderer/uimanager/UIManager.h>
namespace facebook::react {
int majorAxisDistanceRaw(
FocusDirection focusDirection,
Rect source,
Rect dest) {
switch (focusDirection) {
case FocusDirection::FocusLeft:
return static_cast<int>(
source.origin.x - (dest.origin.x + dest.size.width));
case FocusDirection::FocusRight:
return static_cast<int>(
dest.origin.x - (source.origin.x + source.size.width));
case FocusDirection::FocusUp:
return static_cast<int>(
source.origin.y - (dest.origin.y + dest.size.height));
case FocusDirection::FocusDown:
return static_cast<int>(
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<int>(abs((source.getMidY() - dest.getMidY())));
case FocusDirection::FocusUp:
case FocusDirection::FocusDown:
// the distance between the center horizontals
return static_cast<int>(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<Rect>& nextRect,
ShadowNode::Shared& nextNode) {
const auto* props =
dynamic_cast<const ViewProps*>(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<FocusDirection> FocusOrderingHelper::resolveFocusDirection(
int direction) {
switch (static_cast<FocusDirection>(direction)) {
case FocusDirection::FocusDown:
case FocusDirection::FocusUp:
case FocusDirection::FocusRight:
case FocusDirection::FocusLeft:
return static_cast<FocusDirection>(direction);
}
return std::nullopt;
}
} // namespace facebook::react
@@ -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 <react/renderer/uimanager/UIManager.h>
#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<Rect>& nextRect,
ShadowNode::Shared& nextNode);
static ShadowNode::Shared findShadowNodeByTagRecursively(
const ShadowNode::Shared& parentShadowNode,
Tag tag);
static std::optional<FocusDirection> resolveFocusDirection(int direction);
};
} // namespace facebook::react