From 4994fe31485a424d082f8abba55ad9862bd472cd Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Tue, 28 Nov 2023 13:49:09 -0800 Subject: [PATCH] Use containing block to adjust absolute child position (#41490) Summary: X-link: https://github.com/facebook/yoga/pull/1472 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/yoga/algorithm/CalculateLayout.cpp | 180 ++++++++++++------ .../ReactCommon/yoga/yoga/style/Style.h | 16 ++ 2 files changed, 136 insertions(+), 60 deletions(-) diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp index 733603d043b230..9b2fcde5a91087 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/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/packages/react-native/ReactCommon/yoga/yoga/style/Style.h b/packages/react-native/ReactCommon/yoga/yoga/style/Style.h index 30f631f279b602..df64a67b522b4f 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/Style.h +++ b/packages/react-native/ReactCommon/yoga/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_ &&