Skip to content

Commit caab26e

Browse files
sherginfacebook-github-bot
authored andcommitted
Fabric: Refinement of LayoutableShadowNode::getRelativeLayoutMetrics
Summary: This diff simplifies the implementation of `LayoutableShadowNode::getRelativeLayoutMetrics`. It fixes a small bug but the most important change is the new interface. Now the function that does measurements accepts a node and a family instead of two nodes. It prevents misuse and misinterpretation of what the function does. The function needs two things to perform measurement: * an ancestor node that defines the tree is being measured and the base node of measurement; * a family of some descendant node being measured relative to the ancestor node. An API that accepts two nodes is misleading because it implies that the given descendant node will be measured (which is not true). Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D21480200 fbshipit-source-id: 9fddc361417fee47bbf66cc7ac2954eb088a3179
1 parent 656db78 commit caab26e

File tree

3 files changed

+101
-64
lines changed

3 files changed

+101
-64
lines changed

ReactCommon/fabric/core/layout/LayoutableShadowNode.cpp

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -17,54 +17,95 @@
1717
namespace facebook {
1818
namespace react {
1919

20-
/*
21-
* `shadowNode` might not be the newest revision of `ShadowNodeFamily`.
22-
* This function looks at `parentNode`'s children and finds one that belongs
23-
* to the same family as `shadowNode`.
24-
*/
25-
static ShadowNode const *findNewestChildInParent(
26-
ShadowNode const &parentNode,
27-
ShadowNode const &shadowNode) {
28-
for (auto const &child : parentNode.getChildren()) {
29-
if (ShadowNode::sameFamily(*child, shadowNode)) {
30-
return child.get();
20+
LayoutMetrics LayoutableShadowNode::computeRelativeLayoutMetrics(
21+
ShadowNodeFamily const &descendantNodeFamily,
22+
LayoutableShadowNode const &ancestorNode,
23+
LayoutInspectingPolicy policy) {
24+
if (&descendantNodeFamily == &ancestorNode.getFamily()) {
25+
// Layout metrics of a node computed relatively to the same node are equal
26+
// to `transform`-ed layout metrics of the node with zero `origin`.
27+
auto layoutMetrics = ancestorNode.getLayoutMetrics();
28+
if (policy.includeTransform) {
29+
layoutMetrics.frame = layoutMetrics.frame * ancestorNode.getTransform();
3130
}
31+
layoutMetrics.frame.origin = {0, 0};
32+
return layoutMetrics;
33+
}
34+
35+
auto ancestors = descendantNodeFamily.getAncestors(ancestorNode);
36+
37+
if (ancestors.size() == 0) {
38+
// Specified nodes do not form an ancestor-descender relationship
39+
// in the same tree. Aborting.
40+
return EmptyLayoutMetrics;
3241
}
33-
return nullptr;
34-
}
3542

36-
static LayoutMetrics calculateOffsetForLayoutMetrics(
37-
LayoutMetrics layoutMetrics,
38-
ShadowNode::AncestorList const &ancestors,
39-
LayoutableShadowNode::LayoutInspectingPolicy const &policy) {
40-
// `AncestorList` starts from the given ancestor node and ends with the parent
41-
// node. We iterate from parent node (reverse iteration) and stop before the
42-
// given ancestor (rend() - 1).
43-
for (auto it = ancestors.rbegin(); it != ancestors.rend() - 1; ++it) {
44-
auto &currentShadowNode = it->first.get();
45-
46-
if (currentShadowNode.getTraits().check(
47-
ShadowNodeTraits::Trait::RootNodeKind)) {
43+
// Step 1.
44+
// Creating a list of nodes that form a chain from the descender node to
45+
// ancestor node inclusively.
46+
auto shadowNodeList = better::small_vector<ShadowNode const *, 16>{};
47+
48+
// Finding the measured node.
49+
// The last element in the `AncestorList` is a pair of a parent of the node
50+
// and an index of this node in the parent's children list.
51+
auto &pair = ancestors.at(ancestors.size() - 1);
52+
auto descendantNode = pair.first.get().getChildren().at(pair.second).get();
53+
54+
// Putting the node inside the list.
55+
// Even if this is a node with a `RootNodeKind` trait, we don't treat it as
56+
// root because we measure it from an outside tree perspective.
57+
shadowNodeList.push_back(descendantNode);
58+
59+
for (auto it = ancestors.rbegin(); it != ancestors.rend(); it++) {
60+
auto &shadowNode = it->first.get();
61+
62+
shadowNodeList.push_back(&shadowNode);
63+
64+
if (shadowNode.getTraits().check(ShadowNodeTraits::Trait::RootNodeKind)) {
65+
// If this is a node with a `RootNodeKind` trait, we need to stop right
66+
// there.
4867
break;
4968
}
69+
}
70+
71+
// Step 2.
72+
// Computing the initial size of the measured node.
73+
auto descendantLayoutableNode =
74+
traitCast<LayoutableShadowNode const *>(descendantNode);
5075

51-
auto layoutableCurrentShadowNode =
52-
dynamic_cast<LayoutableShadowNode const *>(&currentShadowNode);
76+
if (!descendantLayoutableNode) {
77+
return EmptyLayoutMetrics;
78+
}
5379

54-
if (!layoutableCurrentShadowNode) {
80+
auto layoutMetrics = descendantLayoutableNode->getLayoutMetrics();
81+
auto &resultFrame = layoutMetrics.frame;
82+
resultFrame.origin = {0, 0};
83+
84+
// Step 3.
85+
// Iterating on a list of nodes computing compound offset.
86+
auto size = shadowNodeList.size();
87+
for (int i = 0; i < size; i++) {
88+
auto currentShadowNode =
89+
traitCast<LayoutableShadowNode const *>(shadowNodeList.at(i));
90+
91+
if (!currentShadowNode) {
5592
return EmptyLayoutMetrics;
5693
}
5794

58-
auto frame = layoutableCurrentShadowNode->getLayoutMetrics().frame;
95+
auto currentFrame = currentShadowNode->getLayoutMetrics().frame;
96+
if (i == size - 1) {
97+
// If it's the last element, its origin is irrelevant.
98+
currentFrame.origin = {0, 0};
99+
}
59100

60101
if (policy.includeTransform) {
61-
layoutMetrics.frame.size = layoutMetrics.frame.size *
62-
layoutableCurrentShadowNode->getTransform();
63-
frame = frame * layoutableCurrentShadowNode->getTransform();
102+
resultFrame.size = resultFrame.size * currentShadowNode->getTransform();
103+
currentFrame = currentFrame * currentShadowNode->getTransform();
64104
}
65105

66-
layoutMetrics.frame.origin += frame.origin;
106+
resultFrame.origin += currentFrame.origin;
67107
}
108+
68109
return layoutMetrics;
69110
}
70111

@@ -109,37 +150,8 @@ Transform LayoutableShadowNode::getTransform() const {
109150
LayoutMetrics LayoutableShadowNode::getRelativeLayoutMetrics(
110151
LayoutableShadowNode const &ancestorLayoutableShadowNode,
111152
LayoutInspectingPolicy policy) const {
112-
auto &ancestorShadowNode =
113-
dynamic_cast<ShadowNode const &>(ancestorLayoutableShadowNode);
114-
auto &shadowNode = dynamic_cast<ShadowNode const &>(*this);
115-
116-
if (ShadowNode::sameFamily(shadowNode, ancestorShadowNode)) {
117-
auto layoutMetrics = getLayoutMetrics();
118-
layoutMetrics.frame.origin = {0, 0};
119-
return layoutMetrics;
120-
}
121-
122-
auto ancestors = shadowNode.getFamily().getAncestors(ancestorShadowNode);
123-
124-
if (ancestors.empty()) {
125-
return EmptyLayoutMetrics;
126-
}
127-
128-
auto newestChild =
129-
findNewestChildInParent(ancestors.rbegin()->first.get(), shadowNode);
130-
131-
if (!newestChild) {
132-
return EmptyLayoutMetrics;
133-
}
134-
135-
auto layoutableNewestChild =
136-
dynamic_cast<LayoutableShadowNode const *>(newestChild);
137-
auto layoutMetrics = layoutableNewestChild->getLayoutMetrics();
138-
if (policy.includeTransform) {
139-
layoutMetrics.frame =
140-
layoutMetrics.frame * layoutableNewestChild->getTransform();
141-
}
142-
return calculateOffsetForLayoutMetrics(layoutMetrics, ancestors, policy);
153+
return computeRelativeLayoutMetrics(
154+
getFamily(), ancestorLayoutableShadowNode, policy);
143155
}
144156

145157
LayoutableShadowNode::UnsharedList

ReactCommon/fabric/core/layout/LayoutableShadowNode.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ class LayoutableShadowNode : public ShadowNode {
5151
using UnsharedList = better::
5252
small_vector<LayoutableShadowNode *, kShadowNodeChildrenSmallVectorSize>;
5353

54+
/*
55+
* Returns layout metrics of a node represented as `descendantNodeFamily`
56+
* computed relatively to given `ancestorNode`. Returns `EmptyLayoutMetrics`
57+
* if the nodes don't form an ancestor-descender relationship in the same
58+
* tree.
59+
*/
60+
static LayoutMetrics computeRelativeLayoutMetrics(
61+
ShadowNodeFamily const &descendantNodeFamily,
62+
LayoutableShadowNode const &ancestorNode,
63+
LayoutInspectingPolicy policy);
64+
5465
/*
5566
* Performs layout of the tree starting from this node. Usually is being
5667
* called on the root node.
@@ -99,6 +110,14 @@ class LayoutableShadowNode : public ShadowNode {
99110
*/
100111
virtual Transform getTransform() const;
101112

113+
/*
114+
* Returns layout metrics relatively to the given ancestor node.
115+
* Uses `computeRelativeLayoutMetrics()` under the hood.
116+
*/
117+
LayoutMetrics getRelativeLayoutMetrics(
118+
ShadowNodeFamily const &descendantNodeFamily,
119+
LayoutInspectingPolicy policy) const;
120+
102121
/*
103122
* Returns layout metrics relatively to the given ancestor node.
104123
*/

ReactCommon/fabric/core/tests/LayoutableShadowNodeTest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedNode) {
135135
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 70);
136136
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 50);
137137
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 100);
138+
139+
nodeAA_->_transform = Transform::Identity();
138140
}
139141

140142
TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) {
@@ -160,6 +162,8 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnTransformedParent) {
160162

161163
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 25);
162164
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 25);
165+
166+
nodeAAA_->_transform = Transform::Identity();
163167
}
164168

165169
TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameNode) {
@@ -189,6 +193,8 @@ TEST_F(LayoutableShadowNodeTest, relativeLayoutMetricsOnSameTransformedNode) {
189193
EXPECT_EQ(relativeLayoutMetrics.frame.origin.y, 0);
190194
EXPECT_EQ(relativeLayoutMetrics.frame.size.width, 200);
191195
EXPECT_EQ(relativeLayoutMetrics.frame.size.height, 400);
196+
197+
nodeA_->_transform = Transform::Identity();
192198
}
193199

194200
TEST_F(LayoutableShadowNodeTest, relativeLayourMetricsOnClonedNode) {

0 commit comments

Comments
 (0)