mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
96659f8e83
Summary: Previously, the touch area could never extend past the parent view bounds. Thus, hitSlop would not have any effect if it extended beyond any ancestor view's view bounds. In this diff, we apply hitSlop when calculating overflowInset. Specifically, we make sure to union the hit slop areas of all children when calculating the contentFrame. overflowInset is then used for hit testing (as in [TouchTargetHelper.java](https://www.internalfb.com/code/fbsource/[b0f630bb24271d8ed543e98ff144590290e19805]/xplat/js/react-native-github/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java?lines=195)). A risk is that other optimizations that may potentially rely on overflowInset beyond hit testing (such as clipToBounds) may not be as efficient with a large hitSlop. Changelog: [General][Fixed] - Fix touch handling so that hitSlop can extend beyond parent view bounds. Reviewed By: javache Differential Revision: D43854774 fbshipit-source-id: 160bef135b8487c28c4ada662577c35a7a36f484
448 lines
22 KiB
C++
448 lines
22 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/componentregistry/ComponentDescriptorProviderRegistry.h>
|
|
#include <react/renderer/components/root/RootComponentDescriptor.h>
|
|
#include <react/renderer/components/view/ViewComponentDescriptor.h>
|
|
#include <react/renderer/element/ComponentBuilder.h>
|
|
#include <react/renderer/element/Element.h>
|
|
#include <react/renderer/element/testUtils.h>
|
|
|
|
namespace facebook::react {
|
|
|
|
// Note: the (x, y) origin is always relative to the parent node. You may use
|
|
// P482342650 to re-create this test case in playground.
|
|
|
|
// *******************************************************┌─ABCD:────┐****
|
|
// *******************************************************│ {70,-50} │****
|
|
// *******************************************************│ {30,60} │****
|
|
// *******************************************************│ │****
|
|
// *******************************************************│ │****
|
|
// *******************┌─A: {0,0}{50,50}──┐****************│ │****
|
|
// *******************│ │****************│ │****
|
|
// *******************│ ┌─AB:──────┐ │****************│ │****
|
|
// *******************│ │ {10,10}{30,90}****************│ │****
|
|
// *******************│ │ ┌─ABC: {10,10}{110,20}──────┤ ├───┐
|
|
// *******************│ │ │ │ │ │
|
|
// *******************│ │ │ └──────────┘ │
|
|
// *******************│ │ └──────┬───┬───────────────────────────────┘
|
|
// *******************│ │ │ │********************************
|
|
// *******************└───┤ ├───┘********************************
|
|
// ***********************│ │************************************
|
|
// ***********************│ │************************************
|
|
// ┌─ABE: {-60,50}{70,20}─┴───┐ │************************************
|
|
// │ │ │************************************
|
|
// │ │ │************************************
|
|
// │ │ │************************************
|
|
// │ │ │************************************
|
|
// └──────────────────────┬───┘ │************************************
|
|
// ***********************│ │************************************
|
|
// ***********************└──────────┘************************************
|
|
|
|
enum TestCase {
|
|
AS_IS,
|
|
CLIPPING,
|
|
HIT_SLOP,
|
|
HIT_SLOP_TRANSFORM_TRANSLATE,
|
|
TRANSFORM_SCALE,
|
|
TRANSFORM_TRANSLATE,
|
|
};
|
|
|
|
class LayoutTest : public ::testing::Test {
|
|
protected:
|
|
ComponentBuilder builder_;
|
|
std::shared_ptr<RootShadowNode> rootShadowNode_;
|
|
std::shared_ptr<ViewShadowNode> viewShadowNodeA_;
|
|
std::shared_ptr<ViewShadowNode> viewShadowNodeAB_;
|
|
std::shared_ptr<ViewShadowNode> viewShadowNodeABC_;
|
|
std::shared_ptr<ViewShadowNode> viewShadowNodeABCD_;
|
|
std::shared_ptr<ViewShadowNode> viewShadowNodeABE_;
|
|
|
|
LayoutTest() : builder_(simpleComponentBuilder()) {}
|
|
|
|
void initialize(TestCase testCase) {
|
|
// clang-format off
|
|
auto element =
|
|
Element<RootShadowNode>()
|
|
.reference(rootShadowNode_)
|
|
.tag(1)
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<RootProps>();
|
|
auto &props = *sharedProps;
|
|
props.layoutConstraints = LayoutConstraints{{0,0}, {500, 500}};
|
|
auto &yogaStyle = props.yogaStyle;
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{200, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{200, YGUnitPoint};
|
|
return sharedProps;
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNodeA_)
|
|
.tag(2)
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
auto &props = *sharedProps;
|
|
auto &yogaStyle = props.yogaStyle;
|
|
yogaStyle.positionType() = YGPositionTypeAbsolute;
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{50, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{50, YGUnitPoint};
|
|
return sharedProps;
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNodeAB_)
|
|
.tag(3)
|
|
.props([=] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
auto &props = *sharedProps;
|
|
auto &yogaStyle = props.yogaStyle;
|
|
yogaStyle.positionType() = YGPositionTypeAbsolute;
|
|
yogaStyle.position()[YGEdgeLeft] = YGValue{10, YGUnitPoint};
|
|
yogaStyle.position()[YGEdgeTop] = YGValue{10, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{30, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{90, YGUnitPoint};
|
|
|
|
if (testCase == TRANSFORM_SCALE) {
|
|
props.transform = props.transform * Transform::Scale(2, 2, 1);
|
|
}
|
|
|
|
if (testCase == TRANSFORM_TRANSLATE || testCase == HIT_SLOP_TRANSFORM_TRANSLATE) {
|
|
props.transform = props.transform * Transform::Translate(10, 10, 0);
|
|
}
|
|
|
|
if (testCase == HIT_SLOP || testCase == HIT_SLOP_TRANSFORM_TRANSLATE) {
|
|
props.hitSlop = EdgeInsets{50, 50, 50, 50};
|
|
}
|
|
|
|
return sharedProps;
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNodeABC_)
|
|
.tag(4)
|
|
.props([=] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
auto &props = *sharedProps;
|
|
auto &yogaStyle = props.yogaStyle;
|
|
|
|
if (testCase == CLIPPING) {
|
|
yogaStyle.overflow() = YGOverflowHidden;
|
|
}
|
|
|
|
yogaStyle.positionType() = YGPositionTypeAbsolute;
|
|
yogaStyle.position()[YGEdgeLeft] = YGValue{10, YGUnitPoint};
|
|
yogaStyle.position()[YGEdgeTop] = YGValue{10, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{110, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{20, YGUnitPoint};
|
|
return sharedProps;
|
|
})
|
|
.children({
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNodeABCD_)
|
|
.tag(5)
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
auto &props = *sharedProps;
|
|
auto &yogaStyle = props.yogaStyle;
|
|
yogaStyle.positionType() = YGPositionTypeAbsolute;
|
|
yogaStyle.position()[YGEdgeLeft] = YGValue{70, YGUnitPoint};
|
|
yogaStyle.position()[YGEdgeTop] = YGValue{-50, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{30, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{60, YGUnitPoint};
|
|
return sharedProps;
|
|
})
|
|
}),
|
|
Element<ViewShadowNode>()
|
|
.reference(viewShadowNodeABE_)
|
|
.tag(6)
|
|
.props([] {
|
|
auto sharedProps = std::make_shared<ViewShadowNodeProps>();
|
|
auto &props = *sharedProps;
|
|
auto &yogaStyle = props.yogaStyle;
|
|
yogaStyle.positionType() = YGPositionTypeAbsolute;
|
|
yogaStyle.position()[YGEdgeLeft] = YGValue{-60, YGUnitPoint};
|
|
yogaStyle.position()[YGEdgeTop] = YGValue{50, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionWidth] = YGValue{70, YGUnitPoint};
|
|
yogaStyle.dimensions()[YGDimensionHeight] = YGValue{20, YGUnitPoint};
|
|
return sharedProps;
|
|
})
|
|
})
|
|
})
|
|
});
|
|
// clang-format on
|
|
|
|
builder_.build(element);
|
|
|
|
rootShadowNode_->layoutIfNeeded();
|
|
}
|
|
};
|
|
|
|
// Test the layout as described above with no extra changes
|
|
TEST_F(LayoutTest, overflowInsetTest) {
|
|
initialize(AS_IS);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, -30);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -80);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -50);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
// Test when box ABC has clipping (aka overflow hidden)
|
|
TEST_F(LayoutTest, overflowInsetWithClippingTest) {
|
|
initialize(CLIPPING);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, 0);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -80);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -50);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
// Test when box AB translate (10, 10, 0) in transform. The parent node's
|
|
// overflowInset will be affected, but the transformed node and its child nodes
|
|
// are not affected. Here is an example:
|
|
//
|
|
// ┌────────────────┐ ┌────────────────┐
|
|
// │Original Layout │ │ Translate AB │
|
|
// └────────────────┘ └────────────────┘
|
|
// ─────▶
|
|
// ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐
|
|
// │ A │ │ A │
|
|
// │ │ │ │ │ │ │ │
|
|
// ─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │
|
|
// │ │ │AB ││ │ │ ┌ ─ ─ ┼ ─ ─ ─ ┬──┴┬ ─ ─ ─ ─ ┤
|
|
// └─────┤ ├┘ └───────┤AB │
|
|
// │ │┌──┴─────────┤ │ │ │ │ │
|
|
// ││ABC │ │┌──┴─────────┐
|
|
// │ │└──┬─────────┤ │ │ │ ││ABC │
|
|
// ┌───ABD───────┴─┐ │ │ │└──┬─────────┘
|
|
// ├─────────────┬─┘ │ │ │ │ ├───ABD───────┴─┐ │ │
|
|
// ─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ └─────────────┬─┘ │
|
|
// └ ┴ ─ ─ ─ ─ ─ ─ ┴───┴ ─ ─ ─ ─ ┘
|
|
|
|
TEST_F(LayoutTest, overflowInsetTransformTranslateTest) {
|
|
initialize(TRANSFORM_TRANSLATE);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
// Change on parent node
|
|
// The top/left values are NOT changing as overflowInset is union of before
|
|
// and after transform layout. In this case, we move to the right and bottom,
|
|
// so the left and top is not changing, while right and bottom values are
|
|
// increased.
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, -30);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -60);
|
|
|
|
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
|
|
|
|
// No change on self node with translate transform
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
// No change on child node
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
// Test when box AB scaled 2X in transform. The parent node's overflowInset will
|
|
// be affected. However, the transformed node and its child nodes only appears
|
|
// to be affected (dashed arrow). Since all transform is cosmetic only, the
|
|
// actual values are NOT changed. It will be converted later when mapping the
|
|
// values to pixels during rendering. Here is an example:
|
|
//
|
|
// ┌────────────────┐ ┌────────────────┐
|
|
// │Original Layout │ │ Scale AB │
|
|
// └────────────────┘ └────────────────┘
|
|
// ─────▶
|
|
// ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐
|
|
// │ A │ │ A │
|
|
// │ │ │ │ ├ ─ ─ ─ ─ ─ ┼ ─ ─┌─────┤─ ─ ─ ─ ─ ┤
|
|
// ─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │AB │ ─ ─ ─▶
|
|
// │ │ │AB ││ │ │ │ │ │ │
|
|
// └─────┤ ├┘ └────┤ │
|
|
// │ │┌──┴─────────┤ │ │ ┌───┴──────────┤
|
|
// ││ABC │ │ │ABC │
|
|
// │ │└──┬─────────┤ │ │ │ │ │
|
|
// ┌───ABD───────┴─┐ │ │ │ └───┬──────────┘
|
|
// ├─────────────┬─┘ │ │ │ ├────────────────┴──┐ │ │
|
|
// ─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ │ ABD │ │
|
|
// ├────────────────┬──┘ │ │
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ┴─────┴ ─ ─ ─ ─ ─
|
|
|
|
TEST_F(LayoutTest, overflowInsetTransformScaleTest) {
|
|
initialize(TRANSFORM_SCALE);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
// Change on parent node when a child view scale up
|
|
// Note that AB scale up from its center point. The numbers are calculated
|
|
// assuming AB's center point is not moving.
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -125);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, -115);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -185);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -95);
|
|
|
|
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
|
|
|
|
// The frame of box AB won't actually scale up. The transform matrix is
|
|
// purely cosmetic and should apply later in mounting phase.
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
|
|
|
|
// No change on self node with scale transform. This may sound a bit
|
|
// surprising, but the overflowInset values will be scaled up via pixel
|
|
// density ratio along with width/height of the view. When we do hit-testing,
|
|
// the overflowInset value will appears to be doubled as expected.
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
// The frame of box ABC won't actually scale up. The transform matrix is
|
|
// purely cosmatic and should apply later in mounting phase.
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
// The overflowInset of ABC won't change either. This may sound a bit
|
|
// surprising, but the overflowInset values will be scaled up via pixel
|
|
// density ratio along with width/height of the view. When we do hit-testing,
|
|
// the overflowInset value will appears to be doubled as expected.
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
TEST_F(LayoutTest, overflowInsetHitSlopTest) {
|
|
initialize(HIT_SLOP);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
// Change on parent node
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -80);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -100);
|
|
|
|
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
|
|
|
|
// No change on self node
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
// No change on child node
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
TEST_F(LayoutTest, overflowInsetHitSlopTransformTranslateTest) {
|
|
initialize(HIT_SLOP_TRANSFORM_TRANSLATE);
|
|
|
|
auto layoutMetricsA = viewShadowNodeA_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsA.frame.size.width, 50);
|
|
EXPECT_EQ(layoutMetricsA.frame.size.height, 50);
|
|
|
|
// Change on parent node
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.left, -50);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsA.overflowInset.bottom, -110);
|
|
|
|
auto layoutMetricsAB = viewShadowNodeAB_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.width, 30);
|
|
EXPECT_EQ(layoutMetricsAB.frame.size.height, 90);
|
|
|
|
// No change on self node
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.left, -60);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.top, -40);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.right, -90);
|
|
EXPECT_EQ(layoutMetricsAB.overflowInset.bottom, 0);
|
|
|
|
auto layoutMetricsABC = viewShadowNodeABC_->getLayoutMetrics();
|
|
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.width, 110);
|
|
EXPECT_EQ(layoutMetricsABC.frame.size.height, 20);
|
|
|
|
// No change on child node
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.left, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.top, -50);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.right, 0);
|
|
EXPECT_EQ(layoutMetricsABC.overflowInset.bottom, 0);
|
|
}
|
|
|
|
} // namespace facebook::react
|