mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
Facebook GitHub Bot
parent
1d5324cba9
commit
eee1d8b11d
-2
@@ -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)
|
||||
|
||||
-52
@@ -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
|
||||
Reference in New Issue
Block a user