From 30c7c0613c0398da65c154cb58c4d02c3fef437c Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 4 Dec 2023 19:35:30 -0800 Subject: [PATCH] New file for layout methods related to absolute children Summary: X-link: https://github.com/facebook/yoga/pull/1495 X-link: https://github.com/facebook/react-native/pull/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 --- .../cpp/yoga/algorithm/AbsoluteLayout.cpp | 352 +++++++++++++++ .../main/cpp/yoga/algorithm/AbsoluteLayout.h | 38 ++ .../cpp/yoga/algorithm/CalculateLayout.cpp | 423 +----------------- .../main/cpp/yoga/algorithm/CalculateLayout.h | 33 ++ lib/yoga/src/main/cpp/yoga/node/Node.cpp | 27 ++ lib/yoga/src/main/cpp/yoga/node/Node.h | 6 + 6 files changed, 475 insertions(+), 404 deletions(-) create mode 100644 lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.cpp create mode 100644 lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.h diff --git a/lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.cpp b/lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.cpp new file mode 100644 index 00000000000..bf3a3354352 --- /dev/null +++ b/lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.cpp @@ -0,0 +1,352 @@ +/* + * 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/lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.h b/lib/yoga/src/main/cpp/yoga/algorithm/AbsoluteLayout.h new file mode 100644 index 00000000000..8544fa5c567 --- /dev/null +++ b/lib/yoga/src/main/cpp/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/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.cpp b/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.cpp index 014cd0f776c..25665dafc55 100644 --- a/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.cpp +++ b/lib/yoga/src/main/cpp/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/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.h b/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.h index 47b05dbe1f4..26f4aa47a9d 100644 --- a/lib/yoga/src/main/cpp/yoga/algorithm/CalculateLayout.h +++ b/lib/yoga/src/main/cpp/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/lib/yoga/src/main/cpp/yoga/node/Node.cpp b/lib/yoga/src/main/cpp/yoga/node/Node.cpp index c10fbfdfe75..f26a588e174 100644 --- a/lib/yoga/src/main/cpp/yoga/node/Node.cpp +++ b/lib/yoga/src/main/cpp/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/lib/yoga/src/main/cpp/yoga/node/Node.h b/lib/yoga/src/main/cpp/yoga/node/Node.h index 81036affea0..aa60b3d8303 100644 --- a/lib/yoga/src/main/cpp/yoga/node/Node.h +++ b/lib/yoga/src/main/cpp/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); }