Skip to content

Commit dde0fda

Browse files
joevilchesfacebook-github-bot
authored andcommitted
New file for layout methods related to absolute children (#1495)
Summary: Pull Request resolved: #1495 X-link: facebook/react-native#41794 This is a copy of D51369722 to make it so that it preserves the file history CalculateLayout.cpp is massive and approaching 3k lines. I added a few large functions dealing with layout of absolute nodes and was thinking it would be nice if that logic was just in its own file so it was more isolated and easier to reason about. So I made AbsoluteLayout.cpp and AbsoluteLayout.h to house this logic. In order for this to work I had to expose calculateLayoutInternal in CalculateLayout.h as layoutAbsoluteChild calls it. This is unideal and I would like to find a better way... I also make LayoutUtils.h to house misc small helper methods as they are called in AbsoluteLayout.cpp and CalculateLayout.cpp Reviewed By: NickGerleman Differential Revision: D51824115 fbshipit-source-id: 9b27449e3c1516492c01e6167a6b2c4568a33807
1 parent 7893c4b commit dde0fda

File tree

6 files changed

+475
-404
lines changed

6 files changed

+475
-404
lines changed

yoga/algorithm/AbsoluteLayout.cpp

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <yoga/algorithm/AbsoluteLayout.h>
9+
#include <yoga/algorithm/Align.h>
10+
#include <yoga/algorithm/BoundAxis.h>
11+
#include <yoga/algorithm/CalculateLayout.h>
12+
#include <yoga/algorithm/ResolveValue.h>
13+
14+
namespace facebook::yoga {
15+
16+
/*
17+
* Absolutely positioned nodes do not participate in flex layout and thus their
18+
* positions can be determined independently from the rest of their siblings.
19+
* For each axis there are essentially two cases:
20+
*
21+
* 1) The node has insets defined. In this case we can just use these to
22+
* determine the position of the node.
23+
* 2) The node does not have insets defined. In this case we look at the style
24+
* of the parent to position the node. Things like justify content and
25+
* align content will move absolute children around. If none of these
26+
* special properties are defined, the child is positioned at the start
27+
* (defined by flex direction) of the leading flex line.
28+
*
29+
* This function does that positioning for the given axis. The spec has more
30+
* information on this topic: https://www.w3.org/TR/css-flexbox-1/#abspos-items
31+
*/
32+
static void positionAbsoluteChild(
33+
const yoga::Node* const containingNode,
34+
const yoga::Node* const parent,
35+
yoga::Node* child,
36+
const Direction direction,
37+
const FlexDirection axis,
38+
const bool isMainAxis,
39+
const float containingBlockWidth,
40+
const float containingBlockHeight) {
41+
const bool isAxisRow = isRow(axis);
42+
const bool shouldCenter = isMainAxis
43+
? parent->getStyle().justifyContent() == Justify::Center
44+
: resolveChildAlignment(parent, child) == Align::Center;
45+
const bool shouldFlexEnd = isMainAxis
46+
? parent->getStyle().justifyContent() == Justify::FlexEnd
47+
: ((resolveChildAlignment(parent, child) == Align::FlexEnd) ^
48+
(parent->getStyle().flexWrap() == Wrap::WrapReverse));
49+
50+
if (child->isFlexEndPositionDefined(axis, direction) &&
51+
!child->isFlexStartPositionDefined(axis, direction)) {
52+
child->setLayoutPosition(
53+
containingNode->getLayout().measuredDimension(dimension(axis)) -
54+
child->getLayout().measuredDimension(dimension(axis)) -
55+
containingNode->getFlexEndBorder(axis, direction) -
56+
child->getFlexEndMargin(
57+
axis,
58+
direction,
59+
isAxisRow ? containingBlockWidth : containingBlockHeight) -
60+
child->getFlexEndPosition(
61+
axis,
62+
direction,
63+
isAxisRow ? containingBlockWidth : containingBlockHeight),
64+
flexStartEdge(axis));
65+
} else if (
66+
!child->isFlexStartPositionDefined(axis, direction) && shouldCenter) {
67+
child->setLayoutPosition(
68+
(parent->getLayout().measuredDimension(dimension(axis)) -
69+
child->getLayout().measuredDimension(dimension(axis))) /
70+
2.0f,
71+
flexStartEdge(axis));
72+
} else if (
73+
!child->isFlexStartPositionDefined(axis, direction) && shouldFlexEnd) {
74+
child->setLayoutPosition(
75+
(parent->getLayout().measuredDimension(dimension(axis)) -
76+
child->getLayout().measuredDimension(dimension(axis))),
77+
flexStartEdge(axis));
78+
} else if (
79+
parent->getConfig()->isExperimentalFeatureEnabled(
80+
ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) &&
81+
child->isFlexStartPositionDefined(axis, direction)) {
82+
child->setLayoutPosition(
83+
child->getFlexStartPosition(
84+
axis,
85+
direction,
86+
containingNode->getLayout().measuredDimension(dimension(axis))) +
87+
containingNode->getFlexStartBorder(axis, direction) +
88+
child->getFlexStartMargin(
89+
axis,
90+
direction,
91+
isAxisRow ? containingBlockWidth : containingBlockHeight),
92+
flexStartEdge(axis));
93+
}
94+
}
95+
96+
void layoutAbsoluteChild(
97+
const yoga::Node* const containingNode,
98+
const yoga::Node* const node,
99+
yoga::Node* const child,
100+
const float containingBlockWidth,
101+
const float containingBlockHeight,
102+
const SizingMode widthMode,
103+
const Direction direction,
104+
LayoutData& layoutMarkerData,
105+
const uint32_t depth,
106+
const uint32_t generationCount) {
107+
const FlexDirection mainAxis =
108+
resolveDirection(node->getStyle().flexDirection(), direction);
109+
const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction);
110+
const bool isMainAxisRow = isRow(mainAxis);
111+
112+
float childWidth = YGUndefined;
113+
float childHeight = YGUndefined;
114+
SizingMode childWidthSizingMode = SizingMode::MaxContent;
115+
SizingMode childHeightSizingMode = SizingMode::MaxContent;
116+
117+
auto marginRow =
118+
child->getMarginForAxis(FlexDirection::Row, containingBlockWidth);
119+
auto marginColumn =
120+
child->getMarginForAxis(FlexDirection::Column, containingBlockWidth);
121+
122+
if (child->styleDefinesDimension(FlexDirection::Row, containingBlockWidth)) {
123+
childWidth =
124+
yoga::resolveValue(
125+
child->getResolvedDimension(Dimension::Width), containingBlockWidth)
126+
.unwrap() +
127+
marginRow;
128+
} else {
129+
// If the child doesn't have a specified width, compute the width based on
130+
// the left/right offsets if they're defined.
131+
if (child->isFlexStartPositionDefined(FlexDirection::Row, direction) &&
132+
child->isFlexEndPositionDefined(FlexDirection::Row, direction)) {
133+
childWidth =
134+
containingNode->getLayout().measuredDimension(Dimension::Width) -
135+
(containingNode->getFlexStartBorder(FlexDirection::Row, direction) +
136+
containingNode->getFlexEndBorder(FlexDirection::Row, direction)) -
137+
(child->getFlexStartPosition(
138+
FlexDirection::Row, direction, containingBlockWidth) +
139+
child->getFlexEndPosition(
140+
FlexDirection::Row, direction, containingBlockWidth));
141+
childWidth = boundAxis(
142+
child,
143+
FlexDirection::Row,
144+
childWidth,
145+
containingBlockWidth,
146+
containingBlockWidth);
147+
}
148+
}
149+
150+
if (child->styleDefinesDimension(
151+
FlexDirection::Column, containingBlockHeight)) {
152+
childHeight = yoga::resolveValue(
153+
child->getResolvedDimension(Dimension::Height),
154+
containingBlockHeight)
155+
.unwrap() +
156+
marginColumn;
157+
} else {
158+
// If the child doesn't have a specified height, compute the height based on
159+
// the top/bottom offsets if they're defined.
160+
if (child->isFlexStartPositionDefined(FlexDirection::Column, direction) &&
161+
child->isFlexEndPositionDefined(FlexDirection::Column, direction)) {
162+
childHeight =
163+
containingNode->getLayout().measuredDimension(Dimension::Height) -
164+
(containingNode->getFlexStartBorder(
165+
FlexDirection::Column, direction) +
166+
containingNode->getFlexEndBorder(FlexDirection::Column, direction)) -
167+
(child->getFlexStartPosition(
168+
FlexDirection::Column, direction, containingBlockHeight) +
169+
child->getFlexEndPosition(
170+
FlexDirection::Column, direction, containingBlockHeight));
171+
childHeight = boundAxis(
172+
child,
173+
FlexDirection::Column,
174+
childHeight,
175+
containingBlockHeight,
176+
containingBlockWidth);
177+
}
178+
}
179+
180+
// Exactly one dimension needs to be defined for us to be able to do aspect
181+
// ratio calculation. One dimension being the anchor and the other being
182+
// flexible.
183+
const auto& childStyle = child->getStyle();
184+
if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) {
185+
if (childStyle.aspectRatio().isDefined()) {
186+
if (yoga::isUndefined(childWidth)) {
187+
childWidth = marginRow +
188+
(childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
189+
} else if (yoga::isUndefined(childHeight)) {
190+
childHeight = marginColumn +
191+
(childWidth - marginRow) / childStyle.aspectRatio().unwrap();
192+
}
193+
}
194+
}
195+
196+
// If we're still missing one or the other dimension, measure the content.
197+
if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) {
198+
childWidthSizingMode = yoga::isUndefined(childWidth)
199+
? SizingMode::MaxContent
200+
: SizingMode::StretchFit;
201+
childHeightSizingMode = yoga::isUndefined(childHeight)
202+
? SizingMode::MaxContent
203+
: SizingMode::StretchFit;
204+
205+
// If the size of the owner is defined then try to constrain the absolute
206+
// child to that size as well. This allows text within the absolute child to
207+
// wrap to the size of its owner. This is the same behavior as many browsers
208+
// implement.
209+
if (!isMainAxisRow && yoga::isUndefined(childWidth) &&
210+
widthMode != SizingMode::MaxContent &&
211+
yoga::isDefined(containingBlockWidth) && containingBlockWidth > 0) {
212+
childWidth = containingBlockWidth;
213+
childWidthSizingMode = SizingMode::FitContent;
214+
}
215+
216+
calculateLayoutInternal(
217+
child,
218+
childWidth,
219+
childHeight,
220+
direction,
221+
childWidthSizingMode,
222+
childHeightSizingMode,
223+
childWidth,
224+
childHeight,
225+
false,
226+
LayoutPassReason::kAbsMeasureChild,
227+
layoutMarkerData,
228+
depth,
229+
generationCount);
230+
childWidth = child->getLayout().measuredDimension(Dimension::Width) +
231+
child->getMarginForAxis(FlexDirection::Row, containingBlockWidth);
232+
childHeight = child->getLayout().measuredDimension(Dimension::Height) +
233+
child->getMarginForAxis(FlexDirection::Column, containingBlockWidth);
234+
}
235+
236+
calculateLayoutInternal(
237+
child,
238+
childWidth,
239+
childHeight,
240+
direction,
241+
SizingMode::StretchFit,
242+
SizingMode::StretchFit,
243+
childWidth,
244+
childHeight,
245+
true,
246+
LayoutPassReason::kAbsLayout,
247+
layoutMarkerData,
248+
depth,
249+
generationCount);
250+
251+
positionAbsoluteChild(
252+
containingNode,
253+
node,
254+
child,
255+
direction,
256+
mainAxis,
257+
true /*isMainAxis*/,
258+
containingBlockWidth,
259+
containingBlockHeight);
260+
positionAbsoluteChild(
261+
containingNode,
262+
node,
263+
child,
264+
direction,
265+
crossAxis,
266+
false /*isMainAxis*/,
267+
containingBlockWidth,
268+
containingBlockHeight);
269+
}
270+
271+
void layoutAbsoluteDescendants(
272+
yoga::Node* containingNode,
273+
yoga::Node* currentNode,
274+
SizingMode widthSizingMode,
275+
Direction currentNodeDirection,
276+
LayoutData& layoutMarkerData,
277+
uint32_t currentDepth,
278+
uint32_t generationCount,
279+
float currentNodeMainOffsetFromContainingBlock,
280+
float currentNodeCrossOffsetFromContainingBlock) {
281+
const FlexDirection mainAxis = resolveDirection(
282+
currentNode->getStyle().flexDirection(), currentNodeDirection);
283+
const FlexDirection crossAxis =
284+
resolveCrossDirection(mainAxis, currentNodeDirection);
285+
for (auto child : currentNode->getChildren()) {
286+
if (child->getStyle().display() == Display::None) {
287+
continue;
288+
} else if (child->getStyle().positionType() == PositionType::Absolute) {
289+
layoutAbsoluteChild(
290+
containingNode,
291+
currentNode,
292+
child,
293+
containingNode->getLayout().measuredDimension(Dimension::Width),
294+
containingNode->getLayout().measuredDimension(Dimension::Height),
295+
widthSizingMode,
296+
currentNodeDirection,
297+
layoutMarkerData,
298+
currentDepth,
299+
generationCount);
300+
301+
const bool isMainAxisRow = isRow(mainAxis);
302+
const bool mainInsetsDefined = isMainAxisRow
303+
? child->getStyle().horizontalInsetsDefined()
304+
: child->getStyle().verticalInsetsDefined();
305+
const bool crossInsetsDefined = isMainAxisRow
306+
? child->getStyle().verticalInsetsDefined()
307+
: child->getStyle().horizontalInsetsDefined();
308+
309+
const float childMainOffsetFromParent = mainInsetsDefined
310+
? (child->getLayout().position(flexStartEdge(mainAxis)) -
311+
currentNodeMainOffsetFromContainingBlock)
312+
: child->getLayout().position(flexStartEdge(mainAxis));
313+
const float childCrossOffsetFromParent = crossInsetsDefined
314+
? (child->getLayout().position(flexStartEdge(crossAxis)) -
315+
currentNodeCrossOffsetFromContainingBlock)
316+
: child->getLayout().position(flexStartEdge(crossAxis));
317+
318+
child->setLayoutPosition(
319+
childMainOffsetFromParent, flexStartEdge(mainAxis));
320+
child->setLayoutPosition(
321+
childCrossOffsetFromParent, flexStartEdge(crossAxis));
322+
323+
if (needsTrailingPosition(mainAxis)) {
324+
setChildTrailingPosition(currentNode, child, mainAxis);
325+
}
326+
if (needsTrailingPosition(crossAxis)) {
327+
setChildTrailingPosition(currentNode, child, crossAxis);
328+
}
329+
} else if (child->getStyle().positionType() == PositionType::Static) {
330+
const Direction childDirection =
331+
child->resolveDirection(currentNodeDirection);
332+
const float childMainOffsetFromContainingBlock =
333+
currentNodeMainOffsetFromContainingBlock +
334+
child->getLayout().position(flexStartEdge(mainAxis));
335+
const float childCrossOffsetFromContainingBlock =
336+
currentNodeCrossOffsetFromContainingBlock +
337+
child->getLayout().position(flexStartEdge(crossAxis));
338+
339+
layoutAbsoluteDescendants(
340+
containingNode,
341+
child,
342+
widthSizingMode,
343+
childDirection,
344+
layoutMarkerData,
345+
currentDepth + 1,
346+
generationCount,
347+
childMainOffsetFromContainingBlock,
348+
childCrossOffsetFromContainingBlock);
349+
}
350+
}
351+
}
352+
} // namespace facebook::yoga

yoga/algorithm/AbsoluteLayout.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <yoga/event/event.h>
11+
#include <yoga/node/Node.h>
12+
13+
namespace facebook::yoga {
14+
15+
void layoutAbsoluteChild(
16+
const yoga::Node* const containingNode,
17+
const yoga::Node* const node,
18+
yoga::Node* const child,
19+
const float containingBlockWidth,
20+
const float containingBlockHeight,
21+
const SizingMode widthMode,
22+
const Direction direction,
23+
LayoutData& layoutMarkerData,
24+
const uint32_t depth,
25+
const uint32_t generationCount);
26+
27+
void layoutAbsoluteDescendants(
28+
yoga::Node* containingNode,
29+
yoga::Node* currentNode,
30+
SizingMode widthSizingMode,
31+
Direction currentNodeDirection,
32+
LayoutData& layoutMarkerData,
33+
uint32_t currentDepth,
34+
uint32_t generationCount,
35+
float currentNodeMainOffsetFromContainingBlock,
36+
float currentNodeCrossOffsetFromContainingBlock);
37+
38+
} // namespace facebook::yoga

0 commit comments

Comments
 (0)