From bbd91446dd7a71e785b0405df68bc87fa1c9f3aa Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Mon, 30 Mar 2020 04:00:43 -0700 Subject: [PATCH] Include transform property when calling getRelativeLayoutMetrics Summary: Changelog: [Internal] Current implementation of `measure` doesn't take transform into account.. So if you had a view which has width and height 100 and had `Scale(0.5, 0.5, 1)` (this will shrink view by half). Calling `getRelativeLayoutMetrics` would report its size being `{100, 100}`. This applies if view's parent has transformation as well, because transformation is applied to all subviews of the view as well. Reviewed By: mdvacca Differential Revision: D20621590 fbshipit-source-id: 2cf902a0494291c821ecada56f810c5e6620db5a --- .../core/layout/LayoutableShadowNode.cpp | 20 +++--- .../core/tests/LayoutableShadowNodeTest.cpp | 63 +++++++++++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp index 2108fd3826..4ae44ed720 100644 --- a/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp +++ b/ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp @@ -55,15 +55,15 @@ static LayoutMetrics calculateOffsetForLayoutMetrics( return EmptyLayoutMetrics; } - auto origin = layoutableCurrentShadowNode->getLayoutMetrics().frame.origin; + auto frame = layoutableCurrentShadowNode->getLayoutMetrics().frame; if (policy.includeTransform) { - // The check for ScrollView will be implemented after we have - // a dedicated trait (part of `ShadowNodeTraits`) for that. - origin = origin * layoutableCurrentShadowNode->getTransform(); + layoutMetrics.frame.size = layoutMetrics.frame.size * + layoutableCurrentShadowNode->getTransform(); + frame = frame * layoutableCurrentShadowNode->getTransform(); } - layoutMetrics.frame.origin += origin; + layoutMetrics.frame.origin += frame.origin; } return layoutMetrics; } @@ -132,9 +132,13 @@ LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics( return EmptyLayoutMetrics; } - auto layoutMetrics = dynamic_cast(newestChild) - ->getLayoutMetrics(); - + auto layoutableNewestChild = + dynamic_cast(newestChild); + auto layoutMetrics = layoutableNewestChild->getLayoutMetrics(); + if (policy.includeTransform) { + layoutMetrics.frame = + layoutMetrics.frame * layoutableNewestChild->getTransform(); + } return calculateOffsetForLayoutMetrics(layoutMetrics, ancestors, policy); } diff --git a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp index 02662038f9..9c13ae700f 100644 --- a/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp +++ b/ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp @@ -71,14 +71,33 @@ class LayoutableShadowNodeTest : public ::testing::Test { familyAAA, traits); + auto familyAAAA = std::make_shared( + ShadowNodeFamilyFragment{ + /* .tag = */ 11, + /* .surfaceId = */ 1, + /* .eventEmitter = */ nullptr, + }, + eventDispatcher_, + componentDescriptor_); + + nodeAAAA_ = std::make_shared( + ShadowNodeFragment{ + /* .props = */ std::make_shared(), + /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), + }, + familyAAAA, + traits); + nodeA_->appendChild(nodeAA_); nodeAA_->appendChild(nodeAAA_); + nodeAAA_->appendChild(nodeAAAA_); } std::shared_ptr eventDispatcher_; std::shared_ptr nodeA_; std::shared_ptr nodeAA_; std::shared_ptr nodeAAA_; + std::shared_ptr nodeAAAA_; TestComponentDescriptor componentDescriptor_; }; @@ -98,6 +117,50 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetrics) { EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20); } +TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) { + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + nodeA_->setLayoutMetrics(layoutMetrics); + + layoutMetrics.frame.origin = {10, 20}; + layoutMetrics.frame.size = {100, 200}; + nodeAA_->_transform = Transform::Scale(0.5, 0.5, 1); + nodeAA_->setLayoutMetrics(layoutMetrics); + + auto relativeLayoutMetrics = nodeAA_->getRelativeLayoutMetrics(*nodeA_, {}); + + EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 35); + EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 70); + + EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 50); + EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 100); +} + +TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) { + auto layoutMetrics = EmptyLayoutMetrics; + layoutMetrics.frame.size = {1000, 1000}; + nodeA_->setLayoutMetrics(layoutMetrics); + nodeAA_->setLayoutMetrics(layoutMetrics); + + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {100, 100}; + nodeAAA_->_transform = Transform::Scale(0.5, 0.5, 1); + nodeAAA_->setLayoutMetrics(layoutMetrics); + + layoutMetrics.frame.origin = {10, 10}; + layoutMetrics.frame.size = {50, 50}; + nodeAAAA_->setLayoutMetrics(layoutMetrics); + + auto relativeLayoutMetrics = + nodeAAAA_->getRelativeLayoutMetrics(*nodeAA_, {}); + + EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 45); + EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 45); + + EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 25); + EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 25); +} + TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) { auto layoutMetrics = EmptyLayoutMetrics; layoutMetrics.frame.origin = {10, 20};