From 6691199295df950121b3f1f7e592f23b6606db32 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 15:21:32 -0800 Subject: [PATCH 1/5] Insets no longer apply to statically positioned nodes (#1454) Summary: X-link: https://github.com/facebook/react-native/pull/41369 One of the most basic aspects of statically positioned nodes is that [insets do not apply to them](https://developer.mozilla.org/en-US/docs/Web/CSS/position#static). So I put a guard inside `Node::relativePosition` where we take that into account when setting the position. Reviewed By: NickGerleman Differential Revision: D50507808 --- gentest/fixtures/YGStaticPositionTest.html | 4 ++-- java/tests/com/facebook/yoga/YGStaticPositionTest.java | 2 -- .../tests/generated/YGStaticPositionTest.test.ts | 4 ++-- tests/generated/YGStaticPositionTest.cpp | 4 ---- yoga/node/Node.cpp | 10 +++++++--- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/gentest/fixtures/YGStaticPositionTest.html b/gentest/fixtures/YGStaticPositionTest.html index e73e35f3a5..30b781172b 100644 --- a/gentest/fixtures/YGStaticPositionTest.html +++ b/gentest/fixtures/YGStaticPositionTest.html @@ -1,11 +1,11 @@ -
+
-
+
diff --git a/java/tests/com/facebook/yoga/YGStaticPositionTest.java b/java/tests/com/facebook/yoga/YGStaticPositionTest.java index e6e96bec81..f41d5dd06e 100644 --- a/java/tests/com/facebook/yoga/YGStaticPositionTest.java +++ b/java/tests/com/facebook/yoga/YGStaticPositionTest.java @@ -26,7 +26,6 @@ public static Iterable nodeFactories() { @Parameterized.Parameter public TestParametrization.NodeFactory mNodeFactory; @Test - @Ignore public void test_static_position_insets_have_no_effect_left_top() { YogaConfig config = YogaConfigFactory.create(); config.setExperimentalFeatureEnabled(YogaExperimentalFeature.ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE, true); @@ -69,7 +68,6 @@ public void test_static_position_insets_have_no_effect_left_top() { } @Test - @Ignore public void test_static_position_insets_have_no_effect_right_bottom() { YogaConfig config = YogaConfigFactory.create(); config.setExperimentalFeatureEnabled(YogaExperimentalFeature.ABSOLUTE_PERCENTAGE_AGAINST_PADDING_EDGE, true); diff --git a/javascript/tests/generated/YGStaticPositionTest.test.ts b/javascript/tests/generated/YGStaticPositionTest.test.ts index 538f86535b..0084609542 100644 --- a/javascript/tests/generated/YGStaticPositionTest.test.ts +++ b/javascript/tests/generated/YGStaticPositionTest.test.ts @@ -25,7 +25,7 @@ import { Wrap, } from 'yoga-layout'; -test.skip('static_position_insets_have_no_effect_left_top', () => { +test('static_position_insets_have_no_effect_left_top', () => { const config = Yoga.Config.create(); let root; @@ -73,7 +73,7 @@ test.skip('static_position_insets_have_no_effect_left_top', () => { config.free(); } }); -test.skip('static_position_insets_have_no_effect_right_bottom', () => { +test('static_position_insets_have_no_effect_right_bottom', () => { const config = Yoga.Config.create(); let root; diff --git a/tests/generated/YGStaticPositionTest.cpp b/tests/generated/YGStaticPositionTest.cpp index 23aea42a22..ba1436e44c 100644 --- a/tests/generated/YGStaticPositionTest.cpp +++ b/tests/generated/YGStaticPositionTest.cpp @@ -12,8 +12,6 @@ #include TEST(YogaTest, static_position_insets_have_no_effect_left_top) { - GTEST_SKIP(); - const YGConfigRef config = YGConfigNew(); YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge, true); @@ -57,8 +55,6 @@ TEST(YogaTest, static_position_insets_have_no_effect_left_top) { } TEST(YogaTest, static_position_insets_have_no_effect_right_bottom) { - GTEST_SKIP(); - const YGConfigRef config = YGConfigNew(); YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge, true); diff --git a/yoga/node/Node.cpp b/yoga/node/Node.cpp index faf812e8cf..63639aafd0 100644 --- a/yoga/node/Node.cpp +++ b/yoga/node/Node.cpp @@ -488,11 +488,16 @@ void Node::setLayoutDimension(float LengthValue, Dimension dimension) { } // If both left and right are defined, then use left. Otherwise return +left or -// -right depending on which is defined. +// -right depending on which is defined. Ignore statically positioned nodes as +// insets do not apply to them. float Node::relativePosition( FlexDirection axis, Direction direction, float axisSize) const { + if (style_.positionType() == PositionType::Static && + !hasErrata(Errata::PositionStaticBehavesLikeRelative)) { + return 0; + } if (isInlineStartPositionDefined(axis, direction)) { return getInlineStartPosition(axis, direction, axisSize); } @@ -514,8 +519,7 @@ void Node::setPosition( const FlexDirection crossAxis = yoga::resolveCrossDirection(mainAxis, directionRespectingRoot); - // Here we should check for `PositionType::Static` and in this case zero inset - // properties (left, right, top, bottom, begin, end). + // In the case of position static these are just 0. See: // https://www.w3.org/TR/css-position-3/#valdef-position-static const float relativePositionMain = relativePosition(mainAxis, directionRespectingRoot, mainSize); From 318b0db92dfd01dbc01f7771193a751f0dda88e0 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 15:21:32 -0800 Subject: [PATCH 2/5] Let containing blocks layout their absolute descendants, not parents (#1470) Summary: X-link: https://github.com/facebook/react-native/pull/41488 The way we plan on implementing `position: static` is by changing how we lay out absolutely positioned nodes. Instead of letting their direct parent lay them out we are going to let their containing block handle it. This is useful because by the time the containing block gets to this step it will already know its size, which is needed to ensure that absolute nodes can get the right value with percentage units. Additionally, it means that we can "translate" the position of the absolute nodes to be relative to their parent fairly easily, instead of some second pass that would not be possible with a different design. This change just gets the core pieces of this process going. It makes it so that containing blocks will layout out absolute descendants that they contain. We also pass in the containing block size to the owner size args for `layoutAbsoluteChild`. This new path will only happen if we have the errata turned off. If there is no positioned ancestor for a given node we just assume the root is. This is not exactly how it works on the web - there is a notion of an initial containing block - but we are not implementing that as of right now. Reviewed By: NickGerleman Differential Revision: D51182593 --- yoga/algorithm/CalculateLayout.cpp | 98 +++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index fe97b4e116..ce745e3943 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -531,6 +531,45 @@ static void layoutAbsoluteChild( } } +static void layoutAbsoluteDescendants( + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float containingBlockWidth, + float containingBlockHeight) { + for (auto child : currentNode->getChildren()) { + if (child->getStyle().display() == Display::None) { + continue; + } else if (child->getStyle().positionType() == PositionType::Absolute) { + layoutAbsoluteChild( + currentNode, + child, + containingBlockWidth, + widthSizingMode, + containingBlockHeight, + currentNodeDirection, + layoutMarkerData, + currentDepth, + generationCount); + } else if (child->getStyle().positionType() == PositionType::Static) { + const Direction childDirection = + child->resolveDirection(currentNodeDirection); + layoutAbsoluteDescendants( + child, + widthSizingMode, + childDirection, + layoutMarkerData, + currentDepth + 1, + generationCount, + containingBlockWidth, + containingBlockHeight); + } + } +} + static void measureNodeWithMeasureFunc( yoga::Node* const node, float availableWidth, @@ -2304,29 +2343,46 @@ static void calculateLayoutImpl( if (performLayout) { // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN - for (auto child : node->getChildren()) { - if (child->getStyle().display() == Display::None || - child->getStyle().positionType() != PositionType::Absolute) { - continue; + if (!node->hasErrata(Errata::PositionStaticBehavesLikeRelative)) { + // Let the containing block layout its absolute descendants. By definition + // the containing block will not be static unless we are at the root. + if (node->getStyle().positionType() != PositionType::Static || + depth == 1) { + layoutAbsoluteDescendants( + node, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, + direction, + layoutMarkerData, + depth, + generationCount, + node->getLayout().measuredDimension(Dimension::Width), + node->getLayout().measuredDimension(Dimension::Height)); } - const bool absolutePercentageAgainstPaddingEdge = - node->getConfig()->isExperimentalFeatureEnabled( - ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge); + } else { + for (auto child : node->getChildren()) { + if (child->getStyle().display() == Display::None || + child->getStyle().positionType() != PositionType::Absolute) { + continue; + } + const bool absolutePercentageAgainstPaddingEdge = + node->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge); - layoutAbsoluteChild( - node, - child, - absolutePercentageAgainstPaddingEdge - ? node->getLayout().measuredDimension(Dimension::Width) - : availableInnerWidth, - isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, - absolutePercentageAgainstPaddingEdge - ? node->getLayout().measuredDimension(Dimension::Height) - : availableInnerHeight, - direction, - layoutMarkerData, - depth, - generationCount); + layoutAbsoluteChild( + node, + child, + absolutePercentageAgainstPaddingEdge + ? node->getLayout().measuredDimension(Dimension::Width) + : availableInnerWidth, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, + absolutePercentageAgainstPaddingEdge + ? node->getLayout().measuredDimension(Dimension::Height) + : availableInnerHeight, + direction, + layoutMarkerData, + depth, + generationCount); + } } // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN From 67c2d665bec0e63e17cf3e54ae1e748effb0855d Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 15:21:32 -0800 Subject: [PATCH 3/5] Allow the containing block to set trailing position of absolute descendants (#1471) Summary: X-link: https://github.com/facebook/react-native/pull/41489 If we are going to allow the containing block to layout its absolute descendants and NOT the direct parent then we need to change step 11 which is concerned with setting the trailing position in the case we are row or column reverse. This is the very last step in the function and is positioned that way because it operates on the assumption that all children have their position set by this time. That is no longer a valid assumption if CBs layout their absolute children. In that case the CB also needs to take care of setting the position here. Because of this problem I moved some things around. It now works like: * If errata is set, the direct parent will set trailing position for all non absolute children in step 11 * If errata is set the CB will set trailing position of absolute descendants after they are laid out inside of layoutAbsoluteDescendants Reviewed By: NickGerleman Differential Revision: D51217291 --- yoga/algorithm/CalculateLayout.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index ce745e3943..733603d043 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -93,6 +93,11 @@ static void setChildTrailingPosition( flexEndEdge(axis)); } +static bool needsTrailingPosition(const FlexDirection axis) { + return axis == FlexDirection::RowReverse || + axis == FlexDirection::ColumnReverse; +} + static void constrainMaxSizeForMode( const yoga::Node* const node, const enum FlexDirection axis, @@ -554,6 +559,17 @@ static void layoutAbsoluteDescendants( layoutMarkerData, currentDepth, generationCount); + + const FlexDirection mainAxis = resolveDirection( + currentNode->getStyle().flexDirection(), currentNodeDirection); + const FlexDirection crossAxis = + resolveCrossDirection(mainAxis, currentNodeDirection); + if (needsTrailingPosition(mainAxis)) { + setChildTrailingPosition(currentNode, child, mainAxis); + } + if (needsTrailingPosition(crossAxis)) { + setChildTrailingPosition(currentNode, child, crossAxis); + } } else if (child->getStyle().positionType() == PositionType::Static) { const Direction childDirection = child->resolveDirection(currentNodeDirection); @@ -2386,16 +2402,18 @@ static void calculateLayoutImpl( } // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN - const bool needsMainTrailingPos = mainAxis == FlexDirection::RowReverse || - mainAxis == FlexDirection::ColumnReverse; - const bool needsCrossTrailingPos = crossAxis == FlexDirection::RowReverse || - crossAxis == FlexDirection::ColumnReverse; + const bool needsMainTrailingPos = needsTrailingPosition(mainAxis); + const bool needsCrossTrailingPos = needsTrailingPosition(crossAxis); - // Set trailing position if necessary. if (needsMainTrailingPos || needsCrossTrailingPos) { for (size_t i = 0; i < childCount; i++) { const auto child = node->getChild(i); - if (child->getStyle().display() == Display::None) { + // Absolute children will be handled by their containing block since we + // cannot guarantee that their positions are set when their parents are + // done with layout. + if (child->getStyle().display() == Display::None || + (!node->hasErrata(Errata::PositionStaticBehavesLikeRelative) && + child->getStyle().positionType() == PositionType::Absolute)) { continue; } if (needsMainTrailingPos) { From 98bccf43f0767180454b562eb5c09e2fc087f713 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 15:21:32 -0800 Subject: [PATCH 4/5] Use containing block to adjust absolute child position (#1472) Summary: X-link: https://github.com/facebook/react-native/pull/41490 This change has most of the logic needed for supporting `position: static`. We do two things here that fix a lot of the broken static test: 1) We pass in the containing node to `layoutAbsoluteChild` and use it to properly position the child in the case that insets are defined. 2) We rewrite the absolute child's position to be relative to it's parent in the event that insets are defined for that child (and thus it is positioned relative to its CB). Yoga's layout position has always be relative to parent, so I feel it is easier to just adjust the coordinates of a node to adhere to that design rather than change the consumers of yoga. The "hard" part of this algorithm is determining how to iterate the offset from the containing block needed to do this translation described above. That is handled in `layoutAbsoluteDescendants`. Reviewed By: NickGerleman Differential Revision: D51224327 --- yoga/algorithm/CalculateLayout.cpp | 180 +++++++++++++++++++---------- yoga/style/Style.h | 16 +++ 2 files changed, 136 insertions(+), 60 deletions(-) diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index 733603d043..9b2fcde5a9 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -319,11 +319,12 @@ static void computeFlexBasisForChild( } static void layoutAbsoluteChild( + const yoga::Node* const containingNode, const yoga::Node* const node, yoga::Node* const child, - const float width, + const float containingBlockWidth, + const float containingBlockHeight, const SizingMode widthMode, - const float height, const Direction direction, LayoutData& layoutMarkerData, const uint32_t depth, @@ -338,48 +339,65 @@ static void layoutAbsoluteChild( SizingMode childWidthSizingMode = SizingMode::MaxContent; SizingMode childHeightSizingMode = SizingMode::MaxContent; - auto marginRow = child->getMarginForAxis(FlexDirection::Row, width); - auto marginColumn = child->getMarginForAxis(FlexDirection::Column, width); + auto marginRow = + child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); + auto marginColumn = + child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); - if (styleDefinesDimension(child, FlexDirection::Row, width)) { + if (styleDefinesDimension(child, FlexDirection::Row, containingBlockWidth)) { childWidth = - yoga::resolveValue(child->getResolvedDimension(Dimension::Width), width) + yoga::resolveValue( + child->getResolvedDimension(Dimension::Width), containingBlockWidth) .unwrap() + marginRow; } else { // If the child doesn't have a specified width, compute the width based on // the left/right offsets if they're defined. - if (child->isInlineStartPositionDefined(FlexDirection::Row, direction) && - child->isInlineEndPositionDefined(FlexDirection::Row, direction)) { - childWidth = node->getLayout().measuredDimension(Dimension::Width) - - (node->getInlineStartBorder(FlexDirection::Row, direction) + - node->getInlineEndBorder(FlexDirection::Row, direction)) - - (child->getInlineStartPosition(FlexDirection::Row, direction, width) + - child->getInlineEndPosition(FlexDirection::Row, direction, width)); + if (child->isFlexStartPositionDefined(FlexDirection::Row) && + child->isFlexEndPositionDefined(FlexDirection::Row)) { childWidth = - boundAxis(child, FlexDirection::Row, childWidth, width, width); + containingNode->getLayout().measuredDimension(Dimension::Width) - + (containingNode->getFlexStartBorder(FlexDirection::Row, direction) + + containingNode->getFlexEndBorder(FlexDirection::Row, direction)) - + (child->getFlexStartPosition( + FlexDirection::Row, containingBlockWidth) + + child->getFlexEndPosition(FlexDirection::Row, containingBlockWidth)); + childWidth = boundAxis( + child, + FlexDirection::Row, + childWidth, + containingBlockWidth, + containingBlockWidth); } } - if (styleDefinesDimension(child, FlexDirection::Column, height)) { + if (styleDefinesDimension( + child, FlexDirection::Column, containingBlockHeight)) { childHeight = yoga::resolveValue( - child->getResolvedDimension(Dimension::Height), height) + child->getResolvedDimension(Dimension::Height), + containingBlockHeight) .unwrap() + marginColumn; } else { // If the child doesn't have a specified height, compute the height based on // the top/bottom offsets if they're defined. - if (child->isInlineStartPositionDefined(FlexDirection::Column, direction) && - child->isInlineEndPositionDefined(FlexDirection::Column, direction)) { - childHeight = node->getLayout().measuredDimension(Dimension::Height) - - (node->getInlineStartBorder(FlexDirection::Column, direction) + - node->getInlineEndBorder(FlexDirection::Column, direction)) - - (child->getInlineStartPosition( - FlexDirection::Column, direction, height) + - child->getInlineEndPosition( - FlexDirection::Column, direction, height)); + if (child->isFlexStartPositionDefined(FlexDirection::Column) && + child->isFlexEndPositionDefined(FlexDirection::Column)) { childHeight = - boundAxis(child, FlexDirection::Column, childHeight, height, width); + containingNode->getLayout().measuredDimension(Dimension::Height) - + (containingNode->getFlexStartBorder( + FlexDirection::Column, direction) + + containingNode->getFlexEndBorder(FlexDirection::Column, direction)) - + (child->getFlexStartPosition( + FlexDirection::Column, containingBlockHeight) + + child->getFlexEndPosition( + FlexDirection::Column, containingBlockHeight)); + childHeight = boundAxis( + child, + FlexDirection::Column, + childHeight, + containingBlockHeight, + containingBlockWidth); } } @@ -413,9 +431,9 @@ static void layoutAbsoluteChild( // wrap to the size of its owner. This is the same behavior as many browsers // implement. if (!isMainAxisRow && yoga::isUndefined(childWidth) && - widthMode != SizingMode::MaxContent && yoga::isDefined(width) && - width > 0) { - childWidth = width; + widthMode != SizingMode::MaxContent && + yoga::isDefined(containingBlockWidth) && containingBlockWidth > 0) { + childWidth = containingBlockWidth; childWidthSizingMode = SizingMode::FitContent; } @@ -434,9 +452,9 @@ static void layoutAbsoluteChild( depth, generationCount); childWidth = child->getLayout().measuredDimension(Dimension::Width) + - child->getMarginForAxis(FlexDirection::Row, width); + child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); childHeight = child->getLayout().measuredDimension(Dimension::Height) + - child->getMarginForAxis(FlexDirection::Column, width); + child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); } calculateLayoutInternal( @@ -457,11 +475,15 @@ static void layoutAbsoluteChild( if (child->isFlexEndPositionDefined(mainAxis) && !child->isFlexStartPositionDefined(mainAxis)) { child->setLayoutPosition( - node->getLayout().measuredDimension(dimension(mainAxis)) - + containingNode->getLayout().measuredDimension(dimension(mainAxis)) - child->getLayout().measuredDimension(dimension(mainAxis)) - - node->getFlexEndBorder(mainAxis, direction) - - child->getFlexEndMargin(mainAxis, isMainAxisRow ? width : height) - - child->getFlexEndPosition(mainAxis, isMainAxisRow ? width : height), + containingNode->getFlexEndBorder(mainAxis, direction) - + child->getFlexEndMargin( + mainAxis, + isMainAxisRow ? containingBlockWidth : containingBlockHeight) - + child->getFlexEndPosition( + mainAxis, + isMainAxisRow ? containingBlockWidth : containingBlockHeight), flexStartEdge(mainAxis)); } else if ( !child->isFlexStartPositionDefined(mainAxis) && @@ -485,25 +507,28 @@ static void layoutAbsoluteChild( child->setLayoutPosition( child->getFlexStartPosition( mainAxis, - node->getLayout().measuredDimension(dimension(mainAxis))) + - node->getFlexStartBorder(mainAxis, direction) + + containingNode->getLayout().measuredDimension( + dimension(mainAxis))) + + containingNode->getFlexStartBorder(mainAxis, direction) + child->getFlexStartMargin( mainAxis, - node->getLayout().measuredDimension(dimension(mainAxis))), + isMainAxisRow ? containingBlockWidth : containingBlockHeight), flexStartEdge(mainAxis)); } if (child->isFlexEndPositionDefined(crossAxis) && !child->isFlexStartPositionDefined(crossAxis)) { child->setLayoutPosition( - node->getLayout().measuredDimension(dimension(crossAxis)) - + containingNode->getLayout().measuredDimension(dimension(crossAxis)) - child->getLayout().measuredDimension(dimension(crossAxis)) - - node->getFlexEndBorder(crossAxis, direction) - - child->getFlexEndMargin(crossAxis, isMainAxisRow ? height : width) - + containingNode->getFlexEndBorder(crossAxis, direction) - + child->getFlexEndMargin( + crossAxis, + isMainAxisRow ? containingBlockHeight : containingBlockWidth) - child->getFlexEndPosition( - crossAxis, isMainAxisRow ? height : width), + crossAxis, + isMainAxisRow ? containingBlockHeight : containingBlockWidth), flexStartEdge(crossAxis)); - } else if ( !child->isFlexStartPositionDefined(crossAxis) && resolveChildAlignment(node, child) == Align::Center) { @@ -521,49 +546,74 @@ static void layoutAbsoluteChild( child->getLayout().measuredDimension(dimension(crossAxis))), flexStartEdge(crossAxis)); } else if ( - node->getConfig()->isExperimentalFeatureEnabled( + containingNode->getConfig()->isExperimentalFeatureEnabled( ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) && child->isFlexStartPositionDefined(crossAxis)) { child->setLayoutPosition( child->getFlexStartPosition( crossAxis, - node->getLayout().measuredDimension(dimension(crossAxis))) + - node->getFlexStartBorder(crossAxis, direction) + + containingNode->getLayout().measuredDimension( + dimension(crossAxis))) + + containingNode->getFlexStartBorder(crossAxis, direction) + child->getFlexStartMargin( crossAxis, - node->getLayout().measuredDimension(dimension(crossAxis))), + isMainAxisRow ? containingBlockHeight : containingBlockWidth), flexStartEdge(crossAxis)); } } static void layoutAbsoluteDescendants( + yoga::Node* containingNode, yoga::Node* currentNode, SizingMode widthSizingMode, Direction currentNodeDirection, LayoutData& layoutMarkerData, uint32_t currentDepth, uint32_t generationCount, - float containingBlockWidth, - float containingBlockHeight) { + float currentNodeMainOffsetFromContainingBlock, + float currentNodeCrossOffsetFromContainingBlock) { + const FlexDirection mainAxis = resolveDirection( + currentNode->getStyle().flexDirection(), currentNodeDirection); + const FlexDirection crossAxis = + resolveCrossDirection(mainAxis, currentNodeDirection); for (auto child : currentNode->getChildren()) { if (child->getStyle().display() == Display::None) { continue; } else if (child->getStyle().positionType() == PositionType::Absolute) { layoutAbsoluteChild( + containingNode, currentNode, child, - containingBlockWidth, + containingNode->getLayout().measuredDimension(Dimension::Width), + containingNode->getLayout().measuredDimension(Dimension::Height), widthSizingMode, - containingBlockHeight, currentNodeDirection, layoutMarkerData, currentDepth, generationCount); - const FlexDirection mainAxis = resolveDirection( - currentNode->getStyle().flexDirection(), currentNodeDirection); - const FlexDirection crossAxis = - resolveCrossDirection(mainAxis, currentNodeDirection); + const bool isMainAxisRow = isRow(mainAxis); + const bool mainInsetsDefined = isMainAxisRow + ? child->getStyle().horizontalInsetsDefined() + : child->getStyle().verticalInsetsDefined(); + const bool crossInsetsDefined = isMainAxisRow + ? child->getStyle().verticalInsetsDefined() + : child->getStyle().horizontalInsetsDefined(); + + const float childMainOffsetFromParent = mainInsetsDefined + ? (child->getLayout().position(flexStartEdge(mainAxis)) - + currentNodeMainOffsetFromContainingBlock) + : child->getLayout().position(flexStartEdge(mainAxis)); + const float childCrossOffsetFromParent = crossInsetsDefined + ? (child->getLayout().position(flexStartEdge(crossAxis)) - + currentNodeCrossOffsetFromContainingBlock) + : child->getLayout().position(flexStartEdge(crossAxis)); + + child->setLayoutPosition( + childMainOffsetFromParent, flexStartEdge(mainAxis)); + child->setLayoutPosition( + childCrossOffsetFromParent, flexStartEdge(crossAxis)); + if (needsTrailingPosition(mainAxis)) { setChildTrailingPosition(currentNode, child, mainAxis); } @@ -573,15 +623,23 @@ static void layoutAbsoluteDescendants( } else if (child->getStyle().positionType() == PositionType::Static) { const Direction childDirection = child->resolveDirection(currentNodeDirection); + const float childMainOffsetFromContainingBlock = + currentNodeMainOffsetFromContainingBlock + + child->getLayout().position(flexStartEdge(mainAxis)); + const float childCrossOffsetFromContainingBlock = + currentNodeCrossOffsetFromContainingBlock + + child->getLayout().position(flexStartEdge(crossAxis)); + layoutAbsoluteDescendants( + containingNode, child, widthSizingMode, childDirection, layoutMarkerData, currentDepth + 1, generationCount, - containingBlockWidth, - containingBlockHeight); + childMainOffsetFromContainingBlock, + childCrossOffsetFromContainingBlock); } } } @@ -2365,14 +2423,15 @@ static void calculateLayoutImpl( if (node->getStyle().positionType() != PositionType::Static || depth == 1) { layoutAbsoluteDescendants( + node, node, isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, direction, layoutMarkerData, depth, generationCount, - node->getLayout().measuredDimension(Dimension::Width), - node->getLayout().measuredDimension(Dimension::Height)); + 0.0f, + 0.0f); } } else { for (auto child : node->getChildren()) { @@ -2385,15 +2444,16 @@ static void calculateLayoutImpl( ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge); layoutAbsoluteChild( + node, node, child, absolutePercentageAgainstPaddingEdge ? node->getLayout().measuredDimension(Dimension::Width) : availableInnerWidth, - isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, absolutePercentageAgainstPaddingEdge ? node->getLayout().measuredDimension(Dimension::Height) : availableInnerHeight, + isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim, direction, layoutMarkerData, depth, diff --git a/yoga/style/Style.h b/yoga/style/Style.h index 530cbb75c3..ea3680aa44 100644 --- a/yoga/style/Style.h +++ b/yoga/style/Style.h @@ -231,6 +231,22 @@ class YG_EXPORT Style { } } + bool horizontalInsetsDefined() const { + return position_[YGEdge::YGEdgeLeft].isDefined() || + position_[YGEdge::YGEdgeRight].isDefined() || + position_[YGEdge::YGEdgeAll].isDefined() || + position_[YGEdge::YGEdgeHorizontal].isDefined() || + position_[YGEdge::YGEdgeStart].isDefined() || + position_[YGEdge::YGEdgeEnd].isDefined(); + } + + bool verticalInsetsDefined() const { + return position_[YGEdge::YGEdgeTop].isDefined() || + position_[YGEdge::YGEdgeBottom].isDefined() || + position_[YGEdge::YGEdgeAll].isDefined() || + position_[YGEdge::YGEdgeVertical].isDefined(); + } + bool operator==(const Style& other) const { return direction_ == other.direction_ && flexDirection_ == other.flexDirection_ && From b80954c0e7a92a6e2402128885c8138d1a904dd8 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 15:21:32 -0800 Subject: [PATCH 5/5] Introduce positionAbsoluteChild (#1473) Summary: X-link: https://github.com/facebook/react-native/pull/41491 To simplify the logic a bit I introduce a new function called `positionAbsoluteChild`. This function will, eventually, be the **sole function that matters** when determining the layout position of an absolute node. Because [absolute nodes do not participate in flex layout](https://drafts.csswg.org/css-flexbox/#abspos-items), we can determine the position of said node independently of its siblings. The only information we need are the node itself, its parent, and its containing block - which we have all of in `layoutAbsoluteChild`. Right now, however, this is purely a BE change with no functionality different. There was a big set of if statements at the end of `layoutAbsoluteChild` that would position the node on the main and cross axis for certain cases. The old code had it so that the main and cross axis had basically the same logic but the code was repeated. This puts that logic, as is, in `positionAbsoluteChild` and calls that from `layoutAbsoluteChild`. I will soon edit this function to actually do what it is envisioned to do (i.e. be the sole place that position is set for absolute nodes). Reviewed By: NickGerleman Differential Revision: D51272855 --- yoga/algorithm/CalculateLayout.cpp | 178 +++++++++++++++-------------- 1 file changed, 90 insertions(+), 88 deletions(-) diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index 9b2fcde5a9..2839826ff2 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -318,6 +318,78 @@ static void computeFlexBasisForChild( child->setLayoutComputedFlexBasisGeneration(generationCount); } +/* + * Absolutely positioned nodes do not participate in flex layout and thus their + * positions can be determined independently from the rest of their siblings. + * For each axis there are essentially two cases: + * + * 1) The node has insets defined. In this case we can just use these to + * determine the position of the node. + * 2) The node does not have insets defined. In this case we look at the style + * of the parent to position the node. Things like justify content and + * align content will move absolute children around. If none of these + * special properties are defined, the child is positioned at the start + * (defined by flex direction) of the leading flex line. + * + * This function does that positioning for the given axis. The spec has more + * information on this topic: https://www.w3.org/TR/css-flexbox-1/#abspos-items + */ +static void positionAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const parent, + yoga::Node* child, + const Direction direction, + const FlexDirection axis, + const bool isMainAxis, + const float containingBlockWidth, + const float containingBlockHeight) { + const bool isAxisRow = isRow(axis); + const bool shouldCenter = isMainAxis + ? parent->getStyle().justifyContent() == Justify::Center + : resolveChildAlignment(parent, child) == Align::Center; + const bool shouldFlexEnd = isMainAxis + ? parent->getStyle().justifyContent() == Justify::FlexEnd + : ((resolveChildAlignment(parent, child) == Align::FlexEnd) ^ + (parent->getStyle().flexWrap() == Wrap::WrapReverse)); + + if (child->isFlexEndPositionDefined(axis) && + !child->isFlexStartPositionDefined(axis)) { + child->setLayoutPosition( + containingNode->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis)) - + containingNode->getFlexEndBorder(axis, direction) - + child->getFlexEndMargin( + axis, + isAxisRow ? containingBlockWidth : containingBlockHeight) - + child->getFlexEndPosition( + axis, isAxisRow ? containingBlockWidth : containingBlockHeight), + flexStartEdge(axis)); + } else if (!child->isFlexStartPositionDefined(axis) && shouldCenter) { + child->setLayoutPosition( + (parent->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis))) / + 2.0f, + flexStartEdge(axis)); + } else if (!child->isFlexStartPositionDefined(axis) && shouldFlexEnd) { + child->setLayoutPosition( + (parent->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis))), + flexStartEdge(axis)); + } else if ( + parent->getConfig()->isExperimentalFeatureEnabled( + ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) && + child->isFlexStartPositionDefined(axis)) { + child->setLayoutPosition( + child->getFlexStartPosition( + axis, + containingNode->getLayout().measuredDimension(dimension(axis))) + + containingNode->getFlexStartBorder(axis, direction) + + child->getFlexStartMargin( + axis, isAxisRow ? containingBlockWidth : containingBlockHeight), + flexStartEdge(axis)); + } +} + static void layoutAbsoluteChild( const yoga::Node* const containingNode, const yoga::Node* const node, @@ -472,94 +544,24 @@ static void layoutAbsoluteChild( depth, generationCount); - if (child->isFlexEndPositionDefined(mainAxis) && - !child->isFlexStartPositionDefined(mainAxis)) { - child->setLayoutPosition( - containingNode->getLayout().measuredDimension(dimension(mainAxis)) - - child->getLayout().measuredDimension(dimension(mainAxis)) - - containingNode->getFlexEndBorder(mainAxis, direction) - - child->getFlexEndMargin( - mainAxis, - isMainAxisRow ? containingBlockWidth : containingBlockHeight) - - child->getFlexEndPosition( - mainAxis, - isMainAxisRow ? containingBlockWidth : containingBlockHeight), - flexStartEdge(mainAxis)); - } else if ( - !child->isFlexStartPositionDefined(mainAxis) && - node->getStyle().justifyContent() == Justify::Center) { - child->setLayoutPosition( - (node->getLayout().measuredDimension(dimension(mainAxis)) - - child->getLayout().measuredDimension(dimension(mainAxis))) / - 2.0f, - flexStartEdge(mainAxis)); - } else if ( - !child->isFlexStartPositionDefined(mainAxis) && - node->getStyle().justifyContent() == Justify::FlexEnd) { - child->setLayoutPosition( - (node->getLayout().measuredDimension(dimension(mainAxis)) - - child->getLayout().measuredDimension(dimension(mainAxis))), - flexStartEdge(mainAxis)); - } else if ( - node->getConfig()->isExperimentalFeatureEnabled( - ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) && - child->isFlexStartPositionDefined(mainAxis)) { - child->setLayoutPosition( - child->getFlexStartPosition( - mainAxis, - containingNode->getLayout().measuredDimension( - dimension(mainAxis))) + - containingNode->getFlexStartBorder(mainAxis, direction) + - child->getFlexStartMargin( - mainAxis, - isMainAxisRow ? containingBlockWidth : containingBlockHeight), - flexStartEdge(mainAxis)); - } - - if (child->isFlexEndPositionDefined(crossAxis) && - !child->isFlexStartPositionDefined(crossAxis)) { - child->setLayoutPosition( - containingNode->getLayout().measuredDimension(dimension(crossAxis)) - - child->getLayout().measuredDimension(dimension(crossAxis)) - - containingNode->getFlexEndBorder(crossAxis, direction) - - child->getFlexEndMargin( - crossAxis, - isMainAxisRow ? containingBlockHeight : containingBlockWidth) - - child->getFlexEndPosition( - crossAxis, - isMainAxisRow ? containingBlockHeight : containingBlockWidth), - flexStartEdge(crossAxis)); - } else if ( - !child->isFlexStartPositionDefined(crossAxis) && - resolveChildAlignment(node, child) == Align::Center) { - child->setLayoutPosition( - (node->getLayout().measuredDimension(dimension(crossAxis)) - - child->getLayout().measuredDimension(dimension(crossAxis))) / - 2.0f, - flexStartEdge(crossAxis)); - } else if ( - !child->isFlexStartPositionDefined(crossAxis) && - ((resolveChildAlignment(node, child) == Align::FlexEnd) ^ - (node->getStyle().flexWrap() == Wrap::WrapReverse))) { - child->setLayoutPosition( - (node->getLayout().measuredDimension(dimension(crossAxis)) - - child->getLayout().measuredDimension(dimension(crossAxis))), - flexStartEdge(crossAxis)); - } else if ( - containingNode->getConfig()->isExperimentalFeatureEnabled( - ExperimentalFeature::AbsolutePercentageAgainstPaddingEdge) && - child->isFlexStartPositionDefined(crossAxis)) { - child->setLayoutPosition( - child->getFlexStartPosition( - crossAxis, - containingNode->getLayout().measuredDimension( - dimension(crossAxis))) + - containingNode->getFlexStartBorder(crossAxis, direction) + - child->getFlexStartMargin( - crossAxis, - isMainAxisRow ? containingBlockHeight : containingBlockWidth), - flexStartEdge(crossAxis)); - } + positionAbsoluteChild( + containingNode, + node, + child, + direction, + mainAxis, + true /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); + positionAbsoluteChild( + containingNode, + node, + child, + direction, + crossAxis, + false /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); } static void layoutAbsoluteDescendants(