From 2b96c3a2234fd37119eed77b70a2b2953312a392 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 12:16:45 -0800 Subject: [PATCH] New file for layout methods related to absolute children (#1480) Summary: X-link: https://github.com/facebook/react-native/pull/41683 `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: D51369722 --- yoga/algorithm/AbsoluteLayout.cpp | 353 ++++++++++++++++++++++++ yoga/algorithm/AbsoluteLayout.h | 38 +++ yoga/algorithm/CalculateLayout.cpp | 423 ++--------------------------- yoga/algorithm/CalculateLayout.h | 33 +++ yoga/node/Node.cpp | 27 ++ yoga/node/Node.h | 6 + 6 files changed, 476 insertions(+), 404 deletions(-) create mode 100644 yoga/algorithm/AbsoluteLayout.cpp create mode 100644 yoga/algorithm/AbsoluteLayout.h diff --git a/yoga/algorithm/AbsoluteLayout.cpp b/yoga/algorithm/AbsoluteLayout.cpp new file mode 100644 index 0000000000..2f03a82265 --- /dev/null +++ b/yoga/algorithm/AbsoluteLayout.cpp @@ -0,0 +1,353 @@ +/* + * 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 +#include +#include +#include +#include + +namespace facebook::yoga { + +/* + * 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, direction) && + !child->isFlexStartPositionDefined(axis, direction)) { + child->setLayoutPosition( + containingNode->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis)) - + containingNode->getFlexEndBorder(axis, direction) - + child->getFlexEndMargin( + axis, + direction, + isAxisRow ? containingBlockWidth : containingBlockHeight) - + child->getFlexEndPosition( + axis, + direction, + isAxisRow ? containingBlockWidth : containingBlockHeight), + flexStartEdge(axis)); + } else if ( + !child->isFlexStartPositionDefined(axis, direction) && shouldCenter) { + child->setLayoutPosition( + (parent->getLayout().measuredDimension(dimension(axis)) - + child->getLayout().measuredDimension(dimension(axis))) / + 2.0f, + flexStartEdge(axis)); + } else if ( + !child->isFlexStartPositionDefined(axis, direction) && 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, direction)) { + child->setLayoutPosition( + child->getFlexStartPosition( + axis, + direction, + containingNode->getLayout().measuredDimension(dimension(axis))) + + containingNode->getFlexStartBorder(axis, direction) + + child->getFlexStartMargin( + axis, + direction, + isAxisRow ? containingBlockWidth : containingBlockHeight), + flexStartEdge(axis)); + } +} + +void layoutAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const node, + yoga::Node* const child, + const float containingBlockWidth, + const float containingBlockHeight, + const SizingMode widthMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount) { + const FlexDirection mainAxis = + resolveDirection(node->getStyle().flexDirection(), direction); + const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); + const bool isMainAxisRow = isRow(mainAxis); + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + SizingMode childWidthSizingMode = SizingMode::MaxContent; + SizingMode childHeightSizingMode = SizingMode::MaxContent; + + auto marginRow = + child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); + auto marginColumn = + child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); + + if (child->styleDefinesDimension(FlexDirection::Row, containingBlockWidth)) { + childWidth = + 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->isFlexStartPositionDefined(FlexDirection::Row, direction) && + child->isFlexEndPositionDefined(FlexDirection::Row, direction)) { + childWidth = + containingNode->getLayout().measuredDimension(Dimension::Width) - + (containingNode->getFlexStartBorder(FlexDirection::Row, direction) + + containingNode->getFlexEndBorder(FlexDirection::Row, direction)) - + (child->getFlexStartPosition( + FlexDirection::Row, direction, containingBlockWidth) + + child->getFlexEndPosition( + FlexDirection::Row, direction, containingBlockWidth)); + childWidth = boundAxis( + child, + FlexDirection::Row, + childWidth, + containingBlockWidth, + containingBlockWidth); + } + } + + if (child->styleDefinesDimension( + FlexDirection::Column, containingBlockHeight)) { + childHeight = yoga::resolveValue( + 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->isFlexStartPositionDefined(FlexDirection::Column, direction) && + child->isFlexEndPositionDefined(FlexDirection::Column, direction)) { + childHeight = + containingNode->getLayout().measuredDimension(Dimension::Height) - + (containingNode->getFlexStartBorder( + FlexDirection::Column, direction) + + containingNode->getFlexEndBorder(FlexDirection::Column, direction)) - + (child->getFlexStartPosition( + FlexDirection::Column, direction, containingBlockHeight) + + child->getFlexEndPosition( + FlexDirection::Column, direction, containingBlockHeight)); + childHeight = boundAxis( + child, + FlexDirection::Column, + childHeight, + containingBlockHeight, + containingBlockWidth); + } + } + + // Exactly one dimension needs to be defined for us to be able to do aspect + // ratio calculation. One dimension being the anchor and the other being + // flexible. + const auto& childStyle = child->getStyle(); + if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) { + if (childStyle.aspectRatio().isDefined()) { + if (yoga::isUndefined(childWidth)) { + childWidth = marginRow + + (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); + } else if (yoga::isUndefined(childHeight)) { + childHeight = marginColumn + + (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) { + childWidthSizingMode = yoga::isUndefined(childWidth) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + childHeightSizingMode = yoga::isUndefined(childHeight) + ? SizingMode::MaxContent + : SizingMode::StretchFit; + + // If the size of the owner is defined then try to constrain the absolute + // child to that size as well. This allows text within the absolute child to + // 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(containingBlockWidth) && containingBlockWidth > 0) { + childWidth = containingBlockWidth; + childWidthSizingMode = SizingMode::FitContent; + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + childWidthSizingMode, + childHeightSizingMode, + childWidth, + childHeight, + false, + LayoutPassReason::kAbsMeasureChild, + layoutMarkerData, + depth, + generationCount); + childWidth = child->getLayout().measuredDimension(Dimension::Width) + + child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); + childHeight = child->getLayout().measuredDimension(Dimension::Height) + + child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); + } + + calculateLayoutInternal( + child, + childWidth, + childHeight, + direction, + SizingMode::StretchFit, + SizingMode::StretchFit, + childWidth, + childHeight, + true, + LayoutPassReason::kAbsLayout, + layoutMarkerData, + depth, + generationCount); + + positionAbsoluteChild( + containingNode, + node, + child, + direction, + mainAxis, + true /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); + positionAbsoluteChild( + containingNode, + node, + child, + direction, + crossAxis, + false /*isMainAxis*/, + containingBlockWidth, + containingBlockHeight); +} + +void layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + 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, + containingNode->getLayout().measuredDimension(Dimension::Width), + containingNode->getLayout().measuredDimension(Dimension::Height), + widthSizingMode, + currentNodeDirection, + layoutMarkerData, + currentDepth, + generationCount); + + 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); + } + if (needsTrailingPosition(crossAxis)) { + setChildTrailingPosition(currentNode, child, crossAxis); + } + } 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, + childMainOffsetFromContainingBlock, + childCrossOffsetFromContainingBlock); + } + } +} + +} // namespace facebook::yoga diff --git a/yoga/algorithm/AbsoluteLayout.h b/yoga/algorithm/AbsoluteLayout.h new file mode 100644 index 0000000000..8544fa5c56 --- /dev/null +++ b/yoga/algorithm/AbsoluteLayout.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +namespace facebook::yoga { + +void layoutAbsoluteChild( + const yoga::Node* const containingNode, + const yoga::Node* const node, + yoga::Node* const child, + const float containingBlockWidth, + const float containingBlockHeight, + const SizingMode widthMode, + const Direction direction, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount); + +void layoutAbsoluteDescendants( + yoga::Node* containingNode, + yoga::Node* currentNode, + SizingMode widthSizingMode, + Direction currentNodeDirection, + LayoutData& layoutMarkerData, + uint32_t currentDepth, + uint32_t generationCount, + float currentNodeMainOffsetFromContainingBlock, + float currentNodeCrossOffsetFromContainingBlock); + +} // namespace facebook::yoga diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index 014cd0f776..25665dafc5 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -50,54 +51,6 @@ bool calculateLayoutInternal( const uint32_t depth, const uint32_t generationCount); -static inline float dimensionWithMargin( - const yoga::Node* const node, - const FlexDirection axis, - const float widthSize) { - return node->getLayout().measuredDimension(dimension(axis)) + - node->getMarginForAxis(axis, widthSize); -} - -static inline bool styleDefinesDimension( - const yoga::Node* const node, - const FlexDirection axis, - const float ownerSize) { - bool isDefined = - yoga::isDefined(node->getResolvedDimension(dimension(axis)).value); - - auto resolvedDimension = node->getResolvedDimension(dimension(axis)); - return !( - resolvedDimension.unit == YGUnitAuto || - resolvedDimension.unit == YGUnitUndefined || - (resolvedDimension.unit == YGUnitPoint && isDefined && - resolvedDimension.value < 0.0f) || - (resolvedDimension.unit == YGUnitPercent && isDefined && - (resolvedDimension.value < 0.0f || yoga::isUndefined(ownerSize)))); -} - -static inline bool isLayoutDimensionDefined( - const yoga::Node* const node, - const FlexDirection axis) { - const float value = node->getLayout().measuredDimension(dimension(axis)); - return yoga::isDefined(value) && value >= 0.0f; -} - -static void setChildTrailingPosition( - const yoga::Node* const node, - yoga::Node* const child, - const FlexDirection axis) { - const float size = child->getLayout().measuredDimension(dimension(axis)); - child->setLayoutPosition( - node->getLayout().measuredDimension(dimension(axis)) - size - - child->getLayout().position(flexStartEdge(axis)), - 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, @@ -152,9 +105,9 @@ static void computeFlexBasisForChild( const FloatOptional resolvedFlexBasis = yoga::resolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize); const bool isRowStyleDimDefined = - styleDefinesDimension(child, FlexDirection::Row, ownerWidth); + child->styleDefinesDimension(FlexDirection::Row, ownerWidth); const bool isColumnStyleDimDefined = - styleDefinesDimension(child, FlexDirection::Column, ownerHeight); + child->styleDefinesDimension(FlexDirection::Column, ownerHeight); if (resolvedFlexBasis.isDefined() && yoga::isDefined(mainAxisSize)) { if (child->getLayout().computedFlexBasis.isUndefined() || @@ -318,343 +271,6 @@ 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, direction) && - !child->isFlexStartPositionDefined(axis, direction)) { - child->setLayoutPosition( - containingNode->getLayout().measuredDimension(dimension(axis)) - - child->getLayout().measuredDimension(dimension(axis)) - - containingNode->getFlexEndBorder(axis, direction) - - child->getFlexEndMargin( - axis, - direction, - isAxisRow ? containingBlockWidth : containingBlockHeight) - - child->getFlexEndPosition( - axis, - direction, - isAxisRow ? containingBlockWidth : containingBlockHeight), - flexStartEdge(axis)); - } else if ( - !child->isFlexStartPositionDefined(axis, direction) && shouldCenter) { - child->setLayoutPosition( - (parent->getLayout().measuredDimension(dimension(axis)) - - child->getLayout().measuredDimension(dimension(axis))) / - 2.0f, - flexStartEdge(axis)); - } else if ( - !child->isFlexStartPositionDefined(axis, direction) && 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, direction)) { - child->setLayoutPosition( - child->getFlexStartPosition( - axis, - direction, - containingNode->getLayout().measuredDimension(dimension(axis))) + - containingNode->getFlexStartBorder(axis, direction) + - child->getFlexStartMargin( - axis, - direction, - isAxisRow ? containingBlockWidth : containingBlockHeight), - flexStartEdge(axis)); - } -} - -static void layoutAbsoluteChild( - const yoga::Node* const containingNode, - const yoga::Node* const node, - yoga::Node* const child, - const float containingBlockWidth, - const float containingBlockHeight, - const SizingMode widthMode, - const Direction direction, - LayoutData& layoutMarkerData, - const uint32_t depth, - const uint32_t generationCount) { - const FlexDirection mainAxis = - resolveDirection(node->getStyle().flexDirection(), direction); - const FlexDirection crossAxis = resolveCrossDirection(mainAxis, direction); - const bool isMainAxisRow = isRow(mainAxis); - - float childWidth = YGUndefined; - float childHeight = YGUndefined; - SizingMode childWidthSizingMode = SizingMode::MaxContent; - SizingMode childHeightSizingMode = SizingMode::MaxContent; - - auto marginRow = - child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); - auto marginColumn = - child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); - - if (styleDefinesDimension(child, FlexDirection::Row, containingBlockWidth)) { - childWidth = - 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->isFlexStartPositionDefined(FlexDirection::Row, direction) && - child->isFlexEndPositionDefined(FlexDirection::Row, direction)) { - childWidth = - containingNode->getLayout().measuredDimension(Dimension::Width) - - (containingNode->getFlexStartBorder(FlexDirection::Row, direction) + - containingNode->getFlexEndBorder(FlexDirection::Row, direction)) - - (child->getFlexStartPosition( - FlexDirection::Row, direction, containingBlockWidth) + - child->getFlexEndPosition( - FlexDirection::Row, direction, containingBlockWidth)); - childWidth = boundAxis( - child, - FlexDirection::Row, - childWidth, - containingBlockWidth, - containingBlockWidth); - } - } - - if (styleDefinesDimension( - child, FlexDirection::Column, containingBlockHeight)) { - childHeight = yoga::resolveValue( - 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->isFlexStartPositionDefined(FlexDirection::Column, direction) && - child->isFlexEndPositionDefined(FlexDirection::Column, direction)) { - childHeight = - containingNode->getLayout().measuredDimension(Dimension::Height) - - (containingNode->getFlexStartBorder( - FlexDirection::Column, direction) + - containingNode->getFlexEndBorder(FlexDirection::Column, direction)) - - (child->getFlexStartPosition( - FlexDirection::Column, direction, containingBlockHeight) + - child->getFlexEndPosition( - FlexDirection::Column, direction, containingBlockHeight)); - childHeight = boundAxis( - child, - FlexDirection::Column, - childHeight, - containingBlockHeight, - containingBlockWidth); - } - } - - // Exactly one dimension needs to be defined for us to be able to do aspect - // ratio calculation. One dimension being the anchor and the other being - // flexible. - const auto& childStyle = child->getStyle(); - if (yoga::isUndefined(childWidth) ^ yoga::isUndefined(childHeight)) { - if (childStyle.aspectRatio().isDefined()) { - if (yoga::isUndefined(childWidth)) { - childWidth = marginRow + - (childHeight - marginColumn) * childStyle.aspectRatio().unwrap(); - } else if (yoga::isUndefined(childHeight)) { - childHeight = marginColumn + - (childWidth - marginRow) / childStyle.aspectRatio().unwrap(); - } - } - } - - // If we're still missing one or the other dimension, measure the content. - if (yoga::isUndefined(childWidth) || yoga::isUndefined(childHeight)) { - childWidthSizingMode = yoga::isUndefined(childWidth) - ? SizingMode::MaxContent - : SizingMode::StretchFit; - childHeightSizingMode = yoga::isUndefined(childHeight) - ? SizingMode::MaxContent - : SizingMode::StretchFit; - - // If the size of the owner is defined then try to constrain the absolute - // child to that size as well. This allows text within the absolute child to - // 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(containingBlockWidth) && containingBlockWidth > 0) { - childWidth = containingBlockWidth; - childWidthSizingMode = SizingMode::FitContent; - } - - calculateLayoutInternal( - child, - childWidth, - childHeight, - direction, - childWidthSizingMode, - childHeightSizingMode, - childWidth, - childHeight, - false, - LayoutPassReason::kAbsMeasureChild, - layoutMarkerData, - depth, - generationCount); - childWidth = child->getLayout().measuredDimension(Dimension::Width) + - child->getMarginForAxis(FlexDirection::Row, containingBlockWidth); - childHeight = child->getLayout().measuredDimension(Dimension::Height) + - child->getMarginForAxis(FlexDirection::Column, containingBlockWidth); - } - - calculateLayoutInternal( - child, - childWidth, - childHeight, - direction, - SizingMode::StretchFit, - SizingMode::StretchFit, - childWidth, - childHeight, - true, - LayoutPassReason::kAbsLayout, - layoutMarkerData, - depth, - generationCount); - - positionAbsoluteChild( - containingNode, - node, - child, - direction, - mainAxis, - true /*isMainAxis*/, - containingBlockWidth, - containingBlockHeight); - positionAbsoluteChild( - containingNode, - node, - child, - direction, - crossAxis, - false /*isMainAxis*/, - containingBlockWidth, - containingBlockHeight); -} - -static void layoutAbsoluteDescendants( - yoga::Node* containingNode, - yoga::Node* currentNode, - SizingMode widthSizingMode, - Direction currentNodeDirection, - LayoutData& layoutMarkerData, - uint32_t currentDepth, - uint32_t generationCount, - 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, - containingNode->getLayout().measuredDimension(Dimension::Width), - containingNode->getLayout().measuredDimension(Dimension::Height), - widthSizingMode, - currentNodeDirection, - layoutMarkerData, - currentDepth, - generationCount); - - 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); - } - if (needsTrailingPosition(crossAxis)) { - setChildTrailingPosition(currentNode, child, crossAxis); - } - } 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, - childMainOffsetFromContainingBlock, - childCrossOffsetFromContainingBlock); - } - } -} - static void measureNodeWithMeasureFunc( yoga::Node* const node, float availableWidth, @@ -1066,8 +682,8 @@ static float distributeFreeSpaceSecondPass( childCrossSize += marginCross; } else if ( !std::isnan(availableInnerCrossDim) && - !styleDefinesDimension( - currentLineChild, crossAxis, availableInnerCrossDim) && + !currentLineChild->styleDefinesDimension( + crossAxis, availableInnerCrossDim) && sizingModeCrossDim == SizingMode::StretchFit && !(isNodeFlexWrap && mainAxisOverflows) && resolveChildAlignment(node, currentLineChild) == Align::Stretch && @@ -1076,8 +692,8 @@ static float distributeFreeSpaceSecondPass( currentLineChild->marginTrailingValue(crossAxis).unit != YGUnitAuto) { childCrossSize = availableInnerCrossDim; childCrossSizingMode = SizingMode::StretchFit; - } else if (!styleDefinesDimension( - currentLineChild, crossAxis, availableInnerCrossDim)) { + } else if (!currentLineChild->styleDefinesDimension( + crossAxis, availableInnerCrossDim)) { childCrossSize = availableInnerCrossDim; childCrossSizingMode = yoga::isUndefined(childCrossSize) ? SizingMode::MaxContent @@ -1114,9 +730,8 @@ static float distributeFreeSpaceSecondPass( &childCrossSizingMode, &childCrossSize); - const bool requiresStretchLayout = - !styleDefinesDimension( - currentLineChild, crossAxis, availableInnerCrossDim) && + const bool requiresStretchLayout = !currentLineChild->styleDefinesDimension( + crossAxis, availableInnerCrossDim) && resolveChildAlignment(node, currentLineChild) == Align::Stretch && currentLineChild->getFlexStartMarginValue(crossAxis).unit != YGUnitAuto && @@ -1481,7 +1096,7 @@ static void justifyMainAxis( // The main dimension is the sum of all the elements dimension plus // the spacing. flexLine.layout.mainDim += - dimensionWithMargin(child, mainAxis, availableInnerWidth); + child->dimensionWithMargin(mainAxis, availableInnerWidth); if (isNodeBaselineLayout) { // If the child is baseline aligned then the cross dimension is @@ -1505,7 +1120,7 @@ static void justifyMainAxis( // when the items are not baseline aligned flexLine.layout.crossDim = yoga::maxOrDefined( flexLine.layout.crossDim, - dimensionWithMargin(child, crossAxis, availableInnerWidth)); + child->dimensionWithMargin(crossAxis, availableInnerWidth)); } } } else if (performLayout) { @@ -2014,8 +1629,8 @@ static void calculateLayoutImpl( child->marginTrailingValue(crossAxis).unit != YGUnitAuto) { // If the child defines a definite size for its cross axis, there's // no need to stretch. - if (!styleDefinesDimension( - child, crossAxis, availableInnerCrossDim)) { + if (!child->styleDefinesDimension( + crossAxis, availableInnerCrossDim)) { float childMainSize = child->getLayout().measuredDimension(dimension(mainAxis)); const auto& childStyle = child->getStyle(); @@ -2082,7 +1697,7 @@ static void calculateLayoutImpl( } } else { const float remainingCrossDim = containerCrossAxis - - dimensionWithMargin(child, crossAxis, availableInnerWidth); + child->dimensionWithMargin(crossAxis, availableInnerWidth); if (child->getFlexStartMarginValue(crossAxis).unit == YGUnitAuto && child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { @@ -2192,7 +1807,7 @@ static void calculateLayoutImpl( if (child->getLineIndex() != i) { break; } - if (isLayoutDimensionDefined(child, crossAxis)) { + if (child->isLayoutDimensionDefined(crossAxis)) { lineHeight = yoga::maxOrDefined( lineHeight, child->getLayout().measuredDimension(dimension(crossAxis)) + @@ -2264,8 +1879,8 @@ static void calculateLayoutImpl( // Remeasure child with the line height as it as been only // measured with the owners height yet. - if (!styleDefinesDimension( - child, crossAxis, availableInnerCrossDim)) { + if (!child->styleDefinesDimension( + crossAxis, availableInnerCrossDim)) { const float childWidth = isMainAxisRow ? (child->getLayout().measuredDimension( Dimension::Width) + @@ -2812,7 +2427,7 @@ void calculateLayout( float width = YGUndefined; SizingMode widthSizingMode = SizingMode::MaxContent; const auto& style = node->getStyle(); - if (styleDefinesDimension(node, FlexDirection::Row, ownerWidth)) { + if (node->styleDefinesDimension(FlexDirection::Row, ownerWidth)) { width = (yoga::resolveValue( node->getResolvedDimension(dimension(FlexDirection::Row)), @@ -2834,7 +2449,7 @@ void calculateLayout( float height = YGUndefined; SizingMode heightSizingMode = SizingMode::MaxContent; - if (styleDefinesDimension(node, FlexDirection::Column, ownerHeight)) { + if (node->styleDefinesDimension(FlexDirection::Column, ownerHeight)) { height = (yoga::resolveValue( node->getResolvedDimension(dimension(FlexDirection::Column)), diff --git a/yoga/algorithm/CalculateLayout.h b/yoga/algorithm/CalculateLayout.h index 47b05dbe1f..26f4aa47a9 100644 --- a/yoga/algorithm/CalculateLayout.h +++ b/yoga/algorithm/CalculateLayout.h @@ -8,6 +8,8 @@ #pragma once #include +#include +#include #include namespace facebook::yoga { @@ -18,4 +20,35 @@ void calculateLayout( const float ownerHeight, const Direction ownerDirection); +bool calculateLayoutInternal( + yoga::Node* const node, + const float availableWidth, + const float availableHeight, + const Direction ownerDirection, + const SizingMode widthSizingMode, + const SizingMode heightSizingMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const LayoutPassReason reason, + LayoutData& layoutMarkerData, + const uint32_t depth, + const uint32_t generationCount); + +inline void setChildTrailingPosition( + const yoga::Node* const node, + yoga::Node* const child, + const FlexDirection axis) { + const float size = child->getLayout().measuredDimension(dimension(axis)); + child->setLayoutPosition( + node->getLayout().measuredDimension(dimension(axis)) - size - + child->getLayout().position(flexStartEdge(axis)), + flexEndEdge(axis)); +} + +inline bool needsTrailingPosition(const FlexDirection axis) { + return axis == FlexDirection::RowReverse || + axis == FlexDirection::ColumnReverse; +} + } // namespace facebook::yoga diff --git a/yoga/node/Node.cpp b/yoga/node/Node.cpp index c10fbfdfe7..f26a588e17 100644 --- a/yoga/node/Node.cpp +++ b/yoga/node/Node.cpp @@ -404,6 +404,33 @@ float Node::baseline(float width, float height) const { return baselineFunc_(this, width, height); } +float Node::dimensionWithMargin( + const FlexDirection axis, + const float widthSize) { + return getLayout().measuredDimension(dimension(axis)) + + getMarginForAxis(axis, widthSize); +} + +bool Node::isLayoutDimensionDefined(const FlexDirection axis) { + const float value = getLayout().measuredDimension(dimension(axis)); + return yoga::isDefined(value) && value >= 0.0f; +} + +bool Node::styleDefinesDimension( + const FlexDirection axis, + const float ownerSize) { + bool isDefined = yoga::isDefined(getResolvedDimension(dimension(axis)).value); + + auto resolvedDimension = getResolvedDimension(dimension(axis)); + return !( + resolvedDimension.unit == YGUnitAuto || + resolvedDimension.unit == YGUnitUndefined || + (resolvedDimension.unit == YGUnitPoint && isDefined && + resolvedDimension.value < 0.0f) || + (resolvedDimension.unit == YGUnitPercent && isDefined && + (resolvedDimension.value < 0.0f || yoga::isUndefined(ownerSize)))); +} + // Setters void Node::setMeasureFunc(YGMeasureFunc measureFunc) { diff --git a/yoga/node/Node.h b/yoga/node/Node.h index 81036affea..aa60b3d830 100644 --- a/yoga/node/Node.h +++ b/yoga/node/Node.h @@ -126,6 +126,12 @@ class YG_EXPORT Node : public ::YGNode { float baseline(float width, float height) const; + float dimensionWithMargin(const FlexDirection axis, const float widthSize); + + bool isLayoutDimensionDefined(const FlexDirection axis); + + bool styleDefinesDimension(const FlexDirection axis, const float ownerSize); + bool hasErrata(Errata errata) const { return config_->hasErrata(errata); }