FindNodeAtPoint now accounts for scrollView offset
Summary: Changelog: [internal] FindNodeAtPoint was not taking transform into account. Reviewed By: JoshuaGross Differential Revision: D20288026 fbshipit-source-id: a8ff09bc265de983e42610c5d23837a2000f9dd2
This commit is contained in:
Родитель
d6484fa180
Коммит
f96dc274d1
|
@ -203,13 +203,14 @@ ShadowNode::Shared LayoutableShadowNode::findNodeAtPoint(
|
|||
return nullptr;
|
||||
}
|
||||
auto frame = layoutableShadowNode->getLayoutMetrics().frame;
|
||||
auto isPointInside = frame.containsPoint(point);
|
||||
auto transformedFrame = frame * layoutableShadowNode->getTransform();
|
||||
auto isPointInside = transformedFrame.containsPoint(point);
|
||||
|
||||
if (!isPointInside) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto newPoint = point - frame.origin;
|
||||
auto newPoint = point - transformedFrame.origin;
|
||||
for (const auto &childShadowNode : node->getChildren()) {
|
||||
auto hitView = findNodeAtPoint(childShadowNode, newPoint);
|
||||
if (hitView) {
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "TestComponent.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
class FindNodeAtPointTest : public ::testing::Test {
|
||||
protected:
|
||||
FindNodeAtPointTest()
|
||||
: eventDispatcher_(std::shared_ptr<EventDispatcher const>()),
|
||||
componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) {
|
||||
auto traits = TestShadowNode::BaseTraits();
|
||||
|
||||
auto familyA = std::make_shared<ShadowNodeFamily>(
|
||||
ShadowNodeFamilyFragment{
|
||||
/* .tag = */ 9,
|
||||
/* .surfaceId = */ 1,
|
||||
/* .eventEmitter = */ nullptr,
|
||||
},
|
||||
eventDispatcher_,
|
||||
componentDescriptor_);
|
||||
|
||||
nodeA_ = std::make_shared<TestShadowNode>(
|
||||
ShadowNodeFragment{
|
||||
/* .props = */ std::make_shared<const TestProps>(),
|
||||
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
},
|
||||
familyA,
|
||||
traits);
|
||||
|
||||
auto familyAA = std::make_shared<ShadowNodeFamily>(
|
||||
ShadowNodeFamilyFragment{
|
||||
/* .tag = */ 10,
|
||||
/* .surfaceId = */ 1,
|
||||
/* .eventEmitter = */ nullptr,
|
||||
},
|
||||
eventDispatcher_,
|
||||
componentDescriptor_);
|
||||
|
||||
nodeAA_ = std::make_shared<TestShadowNode>(
|
||||
ShadowNodeFragment{
|
||||
/* .props = */ std::make_shared<const TestProps>(),
|
||||
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
},
|
||||
familyAA,
|
||||
traits);
|
||||
|
||||
auto familyAAA = std::make_shared<ShadowNodeFamily>(
|
||||
ShadowNodeFamilyFragment{
|
||||
/* .tag = */ 11,
|
||||
/* .surfaceId = */ 1,
|
||||
/* .eventEmitter = */ nullptr,
|
||||
},
|
||||
eventDispatcher_,
|
||||
componentDescriptor_);
|
||||
|
||||
nodeAAA_ = std::make_shared<TestShadowNode>(
|
||||
ShadowNodeFragment{
|
||||
/* .props = */ std::make_shared<const TestProps>(),
|
||||
/* .children = */ ShadowNode::emptySharedShadowNodeSharedList(),
|
||||
},
|
||||
familyAAA,
|
||||
traits);
|
||||
|
||||
nodeA_->appendChild(nodeAA_);
|
||||
nodeAA_->appendChild(nodeAAA_);
|
||||
|
||||
auto layoutMetrics = EmptyLayoutMetrics;
|
||||
|
||||
layoutMetrics.frame = facebook::react::Rect{
|
||||
facebook::react::Point{0, 0}, facebook::react::Size{1000, 1000}};
|
||||
nodeA_->setLayoutMetrics(layoutMetrics);
|
||||
|
||||
layoutMetrics.frame = facebook::react::Rect{
|
||||
facebook::react::Point{100, 100}, facebook::react::Size{100, 100}};
|
||||
nodeAA_->setLayoutMetrics(layoutMetrics);
|
||||
|
||||
layoutMetrics.frame = facebook::react::Rect{facebook::react::Point{10, 10},
|
||||
facebook::react::Size{10, 10}};
|
||||
nodeAAA_->setLayoutMetrics(layoutMetrics);
|
||||
}
|
||||
|
||||
std::shared_ptr<EventDispatcher const> eventDispatcher_;
|
||||
std::shared_ptr<TestShadowNode> nodeA_;
|
||||
std::shared_ptr<TestShadowNode> nodeAA_;
|
||||
std::shared_ptr<TestShadowNode> nodeAAA_;
|
||||
TestComponentDescriptor componentDescriptor_;
|
||||
};
|
||||
|
||||
TEST_F(FindNodeAtPointTest, withoutTransform) {
|
||||
EXPECT_EQ(
|
||||
LayoutableShadowNode::findNodeAtPoint(nodeA_, {115, 115}), nodeAAA_);
|
||||
EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {105, 105}), nodeAA_);
|
||||
EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {900, 900}), nodeA_);
|
||||
EXPECT_EQ(
|
||||
LayoutableShadowNode::findNodeAtPoint(nodeA_, {1001, 1001}), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(FindNodeAtPointTest, viewIsTranslated) {
|
||||
nodeA_->_transform =
|
||||
Transform::Identity() * Transform::Translate(-100, -100, 0);
|
||||
|
||||
EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {15, 15}), nodeAAA_);
|
||||
EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {5, 5}), nodeAA_);
|
||||
}
|
||||
|
||||
TEST_F(FindNodeAtPointTest, viewIsScaled) {
|
||||
nodeAAA_->_transform = Transform::Identity() * Transform::Scale(0.5, 0.5, 0);
|
||||
|
||||
EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {116, 116}), nodeAA_);
|
||||
}
|
|
@ -53,9 +53,15 @@ class TestShadowNode : public ConcreteViewShadowNode<
|
|||
public:
|
||||
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
||||
|
||||
bool setLayoutMetrics(LayoutMetrics layoutMetrics) {
|
||||
Transform _transform{Transform::Identity()};
|
||||
|
||||
bool setLayoutMetrics(LayoutMetrics layoutMetrics) override {
|
||||
return YogaLayoutableShadowNode::setLayoutMetrics(layoutMetrics);
|
||||
}
|
||||
|
||||
Transform getTransform() const override {
|
||||
return _transform;
|
||||
}
|
||||
};
|
||||
|
||||
class TestComponentDescriptor
|
||||
|
|
|
@ -128,10 +128,39 @@ void UIManager::clearJSResponder() const {
|
|||
}
|
||||
}
|
||||
|
||||
ShadowNode::Shared const *UIManager::getNewestCloneOfShadowNode(
|
||||
ShadowNode::Shared const &shadowNode) const {
|
||||
auto findNewestChildInParent =
|
||||
[&](auto const &parentNode) -> ShadowNode::Shared const * {
|
||||
for (auto const &child : parentNode.getChildren()) {
|
||||
if (ShadowNode::sameFamily(*child, *shadowNode)) {
|
||||
return &child;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
ShadowNode const *ancestorShadowNode;
|
||||
shadowTreeRegistry_.visit(
|
||||
shadowNode->getSurfaceId(), [&](ShadowTree const &shadowTree) {
|
||||
shadowTree.tryCommit(
|
||||
[&](RootShadowNode::Shared const &oldRootShadowNode) {
|
||||
ancestorShadowNode = oldRootShadowNode.get();
|
||||
return nullptr;
|
||||
},
|
||||
stateReconciliationEnabled_);
|
||||
});
|
||||
|
||||
auto ancestors = shadowNode->getFamily().getAncestors(*ancestorShadowNode);
|
||||
|
||||
return findNewestChildInParent(ancestors.rbegin()->first.get());
|
||||
}
|
||||
|
||||
ShadowNode::Shared UIManager::findNodeAtPoint(
|
||||
const ShadowNode::Shared &node,
|
||||
ShadowNode::Shared const &node,
|
||||
Point point) const {
|
||||
return LayoutableShadowNode::findNodeAtPoint(node, point);
|
||||
return LayoutableShadowNode::findNodeAtPoint(
|
||||
*getNewestCloneOfShadowNode(node), point);
|
||||
}
|
||||
|
||||
void UIManager::setNativeProps(
|
||||
|
|
|
@ -91,9 +91,12 @@ class UIManager final : public ShadowTreeDelegate {
|
|||
void clearJSResponder() const;
|
||||
|
||||
ShadowNode::Shared findNodeAtPoint(
|
||||
const ShadowNode::Shared &shadowNode,
|
||||
ShadowNode::Shared const &shadowNode,
|
||||
Point point) const;
|
||||
|
||||
ShadowNode::Shared const *getNewestCloneOfShadowNode(
|
||||
ShadowNode::Shared const &shadowNode) const;
|
||||
|
||||
/*
|
||||
* Returns layout metrics of given `shadowNode` relative to
|
||||
* `ancestorShadowNode` (relative to the root node in case if provided
|
||||
|
|
Загрузка…
Ссылка в новой задаче