diff --git a/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.cpp b/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.cpp index 8691181b171..c21c6d9128a 100644 --- a/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.cpp +++ b/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.cpp @@ -37,11 +37,9 @@ void ScrollViewShadowNode::layout(LayoutContext layoutContext) { updateStateIfNeeded(); } -Transform ScrollViewShadowNode::getTransform() const { - auto transform = ConcreteViewShadowNode::getTransform(); +Point ScrollViewShadowNode::getContentOriginOffset() const { auto contentOffset = getStateData().contentOffset; - return transform * - Transform::Translate(-contentOffset.x, -contentOffset.y, 0); + return {-contentOffset.x, -contentOffset.y}; } } // namespace react diff --git a/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.h b/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.h index 25ac5045ab2..b448f2964db 100644 --- a/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.h +++ b/ReactCommon/fabric/components/scrollview/ScrollViewShadowNode.h @@ -32,7 +32,7 @@ class ScrollViewShadowNode final : public ConcreteViewShadowNode< #pragma mark - LayoutableShadowNode void layout(LayoutContext layoutContext) override; - Transform getTransform() const override; + Point getContentOriginOffset() const override; private: void updateStateIfNeeded(); diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp index 7841a03a29d..710f51a524c 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp @@ -84,7 +84,7 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics( // Step 3. // Iterating on a list of nodes computing compound offset. auto size = shadowNodeList.size(); - for (int i = 0; i < size; i++) { + for (size_t i = 0; i < size; i++) { auto currentShadowNode = traitCast(shadowNodeList.at(i)); @@ -104,6 +104,10 @@ LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics( } resultFrame.origin += currentFrame.origin; + + if (i != 0 && policy.includeTransform) { + resultFrame.origin += currentShadowNode->getContentOriginOffset(); + } } return layoutMetrics; @@ -147,6 +151,10 @@ Transform LayoutableShadowNode::getTransform() const { return Transform::Identity(); } +Point LayoutableShadowNode::getContentOriginOffset() const { + return {0, 0}; +} + LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics( LayoutableShadowNode const &ancestorLayoutableShadowNode, LayoutInspectingPolicy policy) const { @@ -217,13 +225,15 @@ 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 * layoutableShadowNode->getTransform(); + auto newPoint = point - transformedFrame.origin - + layoutableShadowNode->getContentOriginOffset(); for (const auto &childShadowNode : node->getChildren()) { auto hitView = findNodeAtPoint(childShadowNode, newPoint); if (hitView) { diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h index 1bcd163ead7..b0c7e176406 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.h +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.h @@ -115,6 +115,13 @@ class LayoutableShadowNode : public ShadowNode { */ virtual Transform getTransform() const; + /* + * Returns offset which is applied to children's origin in + * `LayoutableShadowNode::getRelativeLayoutMetrics` and + * `LayoutableShadowNode::findNodeAtPoint`. + */ + virtual Point getContentOriginOffset() const; + /* * Returns layout metrics relatively to the given ancestor node. * Uses `computeRelativeLayoutMetrics()` under the hood. diff --git a/ReactCommon/fabric/core/tests/FindNodeAtPointTest.cpp b/ReactCommon/fabric/core/tests/FindNodeAtPointTest.cpp index ec5540a6bad..79787b4bf9b 100644 --- a/ReactCommon/fabric/core/tests/FindNodeAtPointTest.cpp +++ b/ReactCommon/fabric/core/tests/FindNodeAtPointTest.cpp @@ -10,6 +10,25 @@ using namespace facebook::react; +/* + *┌─────────────────────────┐ + *│nodeA_ │ + *│ │ + *│ │ + *│ │ + *│ │ + *│ │ + *│ │ + *│ ┌────────────────┐ │ + *│ │nodeAA_ │ │ + *│ │ │ │ + *│ │ ┌───────┐ │ │ + *│ │ │nodeAA_│ │ │ + *│ │ │ │ │ │ + *│ │ └───────┘ │ │ + *│ └────────────────┘ │ + *└─────────────────────────┘ + */ class FindNodeAtPointTest : public ::testing::Test { protected: FindNodeAtPointTest() @@ -102,21 +121,19 @@ TEST_F(FindNodeAtPointTest, withoutTransform) { LayoutableShadowNode::findNodeAtPoint(nodeA_, {1001, 1001}), nullptr); } -// Uncomment once T69368852 is resolved. -// TEST_F(FindNodeAtPointTest, viewIsTranslated) { -// nodeA_->_transform = -// Transform::Identity() * Transform::Translate(-100, -100, 0); +TEST_F(FindNodeAtPointTest, viewIsTranslated) { + nodeA_->_contentOriginOffset = {-100, -100}; -// EXPECT_EQ( -// LayoutableShadowNode::findNodeAtPoint(nodeA_, {15, 15})->getTag(), -// nodeAAA_->getTag()); -// EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {5, 5}), nodeAA_); -// } + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(nodeA_, {15, 15})->getTag(), + nodeAAA_->getTag()); + EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {5, 5}), nodeAA_); +} -// TEST_F(FindNodeAtPointTest, viewIsScaled) { -// nodeAAA_->_transform = Transform::Identity() * Transform::Scale(0.5, 0.5, -// 0); +TEST_F(FindNodeAtPointTest, viewIsScaled) { + nodeAAA_->_transform = Transform::Identity() * Transform::Scale(0.5, 0.5, 0); -// EXPECT_EQ(LayoutableShadowNode::findNodeAtPoint(nodeA_, {119, -// 119})->getTag(), nodeAA_->getTag()); -// } + EXPECT_EQ( + LayoutableShadowNode::findNodeAtPoint(nodeA_, {119, 119})->getTag(), + nodeAA_->getTag()); +} diff --git a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp index 924c7adad05..e7de41aa74b 100644 --- a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp @@ -130,6 +130,35 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetrics) { EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20); } +/* + * ┌────────┐ + * │nodeA_ │ + * │ ┌─────┴───┐ + * │ │nodeAA_ │ + * │ │ │ + * └──┤ │ + * │ │ + * └─────────┘ + */ +TEST_F(LayoutableShadowNodeTest, contentOriginOffset) { + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.origin = {10, 20}; + layoutMetrics.frame.size = {100, 200}; + nodeA_->_contentOriginOffset = {10, 10}; + nodeA_->setLayoutMetrics(layoutMetrics); + nodeAA_->setLayoutMetrics(layoutMetrics); + + auto relativeLayoutMetrics = nodeAA_->getRelativeLayoutMetrics(*nodeA_, {}); + + EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 20); + EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 30); + + relativeLayoutMetrics = nodeAA_->getRelativeLayoutMetrics(*nodeA_, {false}); + + EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10); + EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20); +} + /* * ┌────────────────────────┐ * │nodeA_ │ @@ -149,6 +178,10 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) { nodeAA_->_transform = Transform::Scale(0.5, 0.5, 1); nodeAA_->setLayoutMetrics(layoutMetrics); + layoutMetrics.frame.origin = {10, 20}; + layoutMetrics.frame.size = {50, 100}; + nodeAAA_->setLayoutMetrics(layoutMetrics); + auto relativeLayoutMetrics = nodeAA_->getRelativeLayoutMetrics(*nodeA_, {}); EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 35); diff --git a/ReactCommon/fabric/core/tests/TestComponent.h b/ReactCommon/fabric/core/tests/TestComponent.h index e71d46ffb1a..5e01fd2286f 100644 --- a/ReactCommon/fabric/core/tests/TestComponent.h +++ b/ReactCommon/fabric/core/tests/TestComponent.h @@ -58,6 +58,12 @@ class TestShadowNode : public ConcreteViewShadowNode< Transform getTransform() const override { return _transform; } + + facebook::react::Point _contentOriginOffset{}; + + facebook::react::Point getContentOriginOffset() const override { + return _contentOriginOffset; + } }; class TestComponentDescriptor