mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
1e4ebf2531
Summary: Calculation of TransformedFrames was a method introduced by D37994809 (https://github.com/facebook/react-native/commit/64528e5faa445907b8287b412c344f30c20fca61) to fix rendering of inverted flat lists. We found that this operation is crashing in fb4a causing the UBN T127619309 This diff creates a feature flag to disable the calculation of TransformedFrames in Layoutable ShadowNodes. **The goal of this diff is to revert the behavior introduced by D37994809 (https://github.com/facebook/react-native/commit/64528e5faa445907b8287b412c344f30c20fca61)** The featureFlag is disabled in fb4a and enabled in react AR (because ReactAr apps relies on the calculation of TransformedFrames and these apps are not affected) The root cause of the bug will be fixed by D38280674 (which still requires more testing and investigation) changelog: [internal] internal Reviewed By: JoshuaGross Differential Revision: D38286857 fbshipit-source-id: 721cd0554ae6a6b369b3f8dbb584160a270d0f18
806 lines
32 KiB
C++
806 lines
32 KiB
C++
/*
|
|
* 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 <gtest/gtest.h>
|
|
#include <react/renderer/element/Element.h>
|
|
#include <react/renderer/element/testUtils.h>
|
|
|
|
#include "TestComponent.h"
|
|
|
|
using namespace facebook::react;
|
|
|
|
// TODO: T127619309 re-enable CalculateTransformedFrames
|
|
bool enableCalculateTransformedFrames = false;
|
|
|
|
/*
|
|
* ┌────────┐
|
|
* │<View> │
|
|
* │ │
|
|
* │ ┌────┴───┐
|
|
* │ │<View> │
|
|
* └───┤ │
|
|
* │ │
|
|
* │ │
|
|
* └────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayoutMetrics) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.reference(childShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
// A is a parent to B, A has origin {10, 10}, B has origin {10, 10}.
|
|
// B's relative origin to A should be {10, 10}.
|
|
// D19447900 has more about the issue.
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 200);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20);
|
|
}
|
|
|
|
/*
|
|
* ┌──────────────┐
|
|
* │<ScrollView> │
|
|
* │ ┌─────┴───┐
|
|
* │ │<View> │
|
|
* │ │ │
|
|
* └────────┤ │
|
|
* │ │
|
|
* └─────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, contentOriginOffset) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
// clang-format off
|
|
auto element =
|
|
Element<ScrollViewShadowNode>()
|
|
.finalize([](ScrollViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.stateData([](ScrollViewState &data) {
|
|
data.contentOffset = {10, 10};
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.reference(childShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 10);
|
|
|
|
relativeLayoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {false});
|
|
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 20);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────────────┐
|
|
* │<View> │
|
|
* │ ┌────────────────┐│
|
|
* │ │<View> ││
|
|
* │ │ ││
|
|
* │ └────────────────┘│
|
|
* └────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {1000, 1000};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::Scale(0.5, 0.5, 1);
|
|
return sharedProps;
|
|
})
|
|
.reference(childShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────────────┐
|
|
* │<Root> │
|
|
* │ ┌─────────────────────┐│
|
|
* │ │ <View> ││
|
|
* │ │ ┌──────────────┐││
|
|
* │ │ │<View> │││
|
|
* │ │ │ ┌──────────┐│││
|
|
* │ │ │ │<View> ││││
|
|
* │ │ │ │ ││││
|
|
* │ │ │ │ ││││
|
|
* │ │ │ └──────────┘│││
|
|
* │ │ └──────────────┘││
|
|
* │ └─────────────────────┘│
|
|
* └────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
// clang-format off
|
|
auto element =
|
|
Element<RootShadowNode>()
|
|
.finalize([](RootShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {900, 900};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::Scale(0.5, 0.5, 1);
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 10};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 10};
|
|
layoutMetrics.frame.size = {50, 50};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────┐
|
|
* │<View> │
|
|
* │ │
|
|
* └────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {100, 200};
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
});
|
|
// clang-format on
|
|
|
|
auto shadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
shadowNode->getFamily(), *shadowNode, {});
|
|
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 200);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────┐
|
|
* │<View> │
|
|
* │ │
|
|
* └────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameTransformedNode) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::Scale(2, 2, 1);
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {100, 200};
|
|
layoutMetrics.frame.origin = {10, 20};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
});
|
|
// clang-format on
|
|
|
|
auto shadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
shadowNode->getFamily(), *shadowNode, {});
|
|
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 200);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 400);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────────────┐
|
|
* │<View> │
|
|
* │ ┌────────────────┐│
|
|
* │ │<View> ││
|
|
* │ │ ││
|
|
* │ └────────────────┘│
|
|
* └────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, relativeLayourMetricsOnClonedNode) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto clonedChildShadowNode =
|
|
std::static_pointer_cast<ViewShadowNode>(childShadowNode->clone({}));
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {50, 60};
|
|
clonedChildShadowNode->setLayoutMetrics(layoutMetrics);
|
|
|
|
parentShadowNode->replaceChild(*childShadowNode, clonedChildShadowNode);
|
|
|
|
auto newRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
EXPECT_EQ(newRelativeLayoutMetrics.frame.size.width, 50);
|
|
EXPECT_EQ(newRelativeLayoutMetrics.frame.size.height, 60);
|
|
}
|
|
|
|
/*
|
|
* ┌─────────────────────────┐
|
|
* │<View> │
|
|
* │ ┌──────────────────────┐│
|
|
* │ │<Modal> ││
|
|
* │ │ ┌───────────┐ ││
|
|
* │ │ │<View> │ ││
|
|
* │ │ │ │ ││
|
|
* │ │ └───────────┘ ││
|
|
* │ └──────────────────────┘│
|
|
* └─────────────────────────┘
|
|
*/
|
|
TEST(
|
|
LayoutableShadowNodeTest,
|
|
relativeLayoutMetricsOnNodesCrossingRootKindNode) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.children({
|
|
Element<ModalHostViewShadowNode>()
|
|
.finalize([](ModalHostViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 10};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {10, 10};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
})
|
|
});
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics(childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
// relativeLayoutMetrics do not include offsset of nodeAA_ because it is a
|
|
// RootKindNode.
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 10);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 10);
|
|
}
|
|
|
|
TEST(LayoutableShadowNodeTest, includeViewportOffset) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto viewShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<RootShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<RootProps>();
|
|
sharedProps->layoutContext.viewportOffset = {10, 20};
|
|
return sharedProps;
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto rootShadowNode = builder.build(element);
|
|
|
|
// `includeViewportOffset` has to work with `includeTransform` enabled and
|
|
// disabled.
|
|
auto layoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
viewShadowNode->getFamily(),
|
|
*rootShadowNode,
|
|
{/* includeTransform = */ false, /* includeViewportOffset = */ true});
|
|
EXPECT_EQ(layoutMetrics.frame.origin.x, 10);
|
|
EXPECT_EQ(layoutMetrics.frame.origin.y, 20);
|
|
|
|
layoutMetrics = LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
viewShadowNode->getFamily(),
|
|
*rootShadowNode,
|
|
{/* includeTransform = */ true, /* includeViewportOffset = */ true});
|
|
EXPECT_EQ(layoutMetrics.frame.origin.x, 10);
|
|
EXPECT_EQ(layoutMetrics.frame.origin.y, 20);
|
|
}
|
|
|
|
/*
|
|
* ┌───────────────────────────────┐
|
|
* │ <View verticallyInverted> │
|
|
* │ │
|
|
* │┌─────────────────────────────┐│
|
|
* ││<View childShadowNode1> ││
|
|
* ││ ││
|
|
* │└─────────────────────────────┘│
|
|
* │┌─────────────────────────────┐│
|
|
* ││<View childShadowNode2> ││
|
|
* ││ ││
|
|
* │└─────────────────────────────┘│
|
|
* └───────────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, invertedVerticalView) {
|
|
auto builder = simpleComponentBuilder();
|
|
auto childShadowNode1 = std::shared_ptr<ViewShadowNode>{};
|
|
auto childShadowNode2 = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
if (!enableCalculateTransformedFrames) {
|
|
return;
|
|
}
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::VerticalInversion(); // Inverted <ScrollView>
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {200, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}).children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode1)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode2)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 100};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
auto scrollShadowNode = builder.build(element);
|
|
|
|
auto firstItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode1->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.y, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
|
|
auto secondItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode2->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.y, 0);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
}
|
|
|
|
/*
|
|
* ┌────────────────────────────────────┐
|
|
* │ <View verticallyInverted> │
|
|
* │ │
|
|
* │ ┌───────────────────────────────┐ │
|
|
* │ │ <View> │ │
|
|
* │ │ │ │
|
|
* │ │┌─────────────────────────────┐│ │
|
|
* │ ││<View childShadowNode1> ││ │
|
|
* │ ││ ││ │
|
|
* │ │└─────────────────────────────┘│ │
|
|
* │ │┌─────────────────────────────┐│ │
|
|
* │ ││<View childShadowNode2> ││ │
|
|
* │ ││ ││ │
|
|
* │ │└─────────────────────────────┘│ │
|
|
* │ └───────────────────────────────┘ │
|
|
* └────────────────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, nestedInvertedVerticalView) {
|
|
auto builder = simpleComponentBuilder();
|
|
auto childShadowNode1 = std::shared_ptr<ViewShadowNode>{};
|
|
auto childShadowNode2 = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
if (!enableCalculateTransformedFrames) {
|
|
return;
|
|
}
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::VerticalInversion(); // Inverted <ScrollView>
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {400, 400};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {100, 50};
|
|
layoutMetrics.frame.size = {200, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}).children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode1)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode2)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 100};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
auto scrollShadowNode = builder.build(element);
|
|
|
|
auto firstItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode1->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.x, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.y, 250);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
|
|
auto secondItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode2->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.x, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.y, 150);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
}
|
|
|
|
/*
|
|
* ┌──────────────────────────────────────┐
|
|
* │ <View horizontallyInverted> │
|
|
* │ │
|
|
* │┌─────────────────┐┌─────────────────┐│
|
|
* ││ <View> ││ <View> ││
|
|
* ││ ││ ││
|
|
* │└─────────────────┘└─────────────────┘│
|
|
* └──────────────────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, invertedHorizontalView) {
|
|
auto builder = simpleComponentBuilder();
|
|
auto childShadowNode1 = std::shared_ptr<ViewShadowNode>{};
|
|
auto childShadowNode2 = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
if (!enableCalculateTransformedFrames) {
|
|
return;
|
|
}
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::HorizontalInversion(); // Inverted <ScrollView>
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {200, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}).children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode1)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode2)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {100, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
auto scrollShadowNode = builder.build(element);
|
|
|
|
auto firstItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode1->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.x, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.y, 0);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
|
|
auto secondItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode2->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.x, 0);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.y, 0);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
}
|
|
|
|
/*
|
|
* ┌──────────────────────────────────────────┐
|
|
* │ <View horizontallyInverted> │
|
|
* │ │
|
|
* │ ┌──────────────────────────────────────┐ │
|
|
* │ │ <View> │ │
|
|
* │ │ │ │
|
|
* │ │┌─────────────────┐┌─────────────────┐│ │
|
|
* │ ││ <View> ││ <View> ││ │
|
|
* │ ││ ││ ││ │
|
|
* │ │└─────────────────┘└─────────────────┘│ │
|
|
* │ └──────────────────────────────────────┘ │
|
|
* └──────────────────────────────────────────┘
|
|
*/
|
|
TEST(LayoutableShadowNodeTest, nestedInvertedHorizontalView) {
|
|
auto builder = simpleComponentBuilder();
|
|
auto childShadowNode1 = std::shared_ptr<ViewShadowNode>{};
|
|
auto childShadowNode2 = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
if (!enableCalculateTransformedFrames) {
|
|
return;
|
|
}
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
sharedProps->transform = Transform::HorizontalInversion(); // Inverted <ScrollView>
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {400, 400};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {50, 100};
|
|
layoutMetrics.frame.size = {200, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}).children({
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode1)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {0, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(childShadowNode2)
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {100, 0};
|
|
layoutMetrics.frame.size = {100, 100};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
auto scrollShadowNode = builder.build(element);
|
|
|
|
auto firstItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode1->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.x, 250);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.origin.y, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(firstItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
|
|
auto secondItemRelativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode2->getFamily(), *scrollShadowNode, {});
|
|
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.x, 150);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.origin.y, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.width, 100);
|
|
EXPECT_EQ(secondItemRelativeLayoutMetrics.frame.size.height, 100);
|
|
}
|
|
|
|
TEST(LayoutableShadowNodeTest, inversedContentOriginOffset) {
|
|
auto builder = simpleComponentBuilder();
|
|
|
|
auto childShadowNode = std::shared_ptr<ViewShadowNode>{};
|
|
|
|
if (!enableCalculateTransformedFrames) {
|
|
return;
|
|
}
|
|
|
|
// clang-format off
|
|
auto element =
|
|
Element<ScrollViewShadowNode>()
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ScrollViewProps>();
|
|
sharedProps->transform = Transform::HorizontalInversion() * Transform::VerticalInversion();
|
|
return sharedProps;
|
|
})
|
|
.finalize([](ScrollViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.size = {300, 350};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.stateData([](ScrollViewState &data) {
|
|
data.contentOffset = {10, 20};
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.finalize([](ViewShadowNode &shadowNode){
|
|
auto layoutMetrics = EmptyLayoutMetrics;
|
|
layoutMetrics.frame.origin = {30, 40};
|
|
layoutMetrics.frame.size = {100, 200};
|
|
shadowNode.setLayoutMetrics(layoutMetrics);
|
|
})
|
|
.reference(childShadowNode)
|
|
});
|
|
// clang-format on
|
|
|
|
auto parentShadowNode = builder.build(element);
|
|
|
|
auto relativeLayoutMetrics =
|
|
LayoutableShadowNode::computeRelativeLayoutMetrics(
|
|
childShadowNode->getFamily(), *parentShadowNode, {});
|
|
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.x, 180);
|
|
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 130);
|
|
}
|