From 3c952abb49cd5084c897aa3dc8d7e93c5cda1494 Mon Sep 17 00:00:00 2001 From: Kamil Paradowski Date: Thu, 19 Mar 2026 10:39:05 -0300 Subject: [PATCH 1/5] feat: calc layout calculations --- enums.py | 1 + java/com/facebook/yoga/YogaUnit.java | 4 +- javascript/src/generated/YGEnums.ts | 2 + tests/StyleTest.cpp | 11 +- yoga/YGEnums.cpp | 2 + yoga/YGEnums.h | 3 +- yoga/YGNodeStyle.cpp | 101 ++++++++++++++ yoga/YGNodeStyle.h | 54 +++++++ yoga/YGValue.h | 33 +++++ yoga/algorithm/AbsoluteLayout.cpp | 51 +++---- yoga/algorithm/BoundAxis.h | 12 +- yoga/algorithm/CalculateLayout.cpp | 169 ++++++++++++---------- yoga/algorithm/FlexLine.cpp | 6 +- yoga/enums/Unit.h | 3 +- yoga/node/Node.cpp | 21 +-- yoga/node/Node.h | 6 +- yoga/style/Style.h | 201 ++++++++++++++++++--------- yoga/style/StyleLength.h | 74 ++++++++-- yoga/style/StyleSizeLength.h | 74 ++++++++-- yoga/style/StyleValueHandle.h | 7 +- yoga/style/StyleValuePool.h | 48 +++++++ 21 files changed, 666 insertions(+), 217 deletions(-) diff --git a/enums.py b/enums.py index ceef4deed3..9a03d5da24 100755 --- a/enums.py +++ b/enums.py @@ -16,6 +16,7 @@ "MaxContent", "FitContent", "Stretch", + "Dynamic", ], "FlexDirection": ["Column", "ColumnReverse", "Row", "RowReverse"], "Justify": [ diff --git a/java/com/facebook/yoga/YogaUnit.java b/java/com/facebook/yoga/YogaUnit.java index 5731a040d9..54238a9b02 100644 --- a/java/com/facebook/yoga/YogaUnit.java +++ b/java/com/facebook/yoga/YogaUnit.java @@ -16,7 +16,8 @@ public enum YogaUnit { AUTO(3), MAX_CONTENT(4), FIT_CONTENT(5), - STRETCH(6); + STRETCH(6), + DYNAMIC(7); private final int mIntValue; @@ -37,6 +38,7 @@ public static YogaUnit fromInt(int value) { case 4: return MAX_CONTENT; case 5: return FIT_CONTENT; case 6: return STRETCH; + case 7: return DYNAMIC; default: throw new IllegalArgumentException("Unknown enum value: " + value); } } diff --git a/javascript/src/generated/YGEnums.ts b/javascript/src/generated/YGEnums.ts index 17878ab0bb..c00049bef3 100644 --- a/javascript/src/generated/YGEnums.ts +++ b/javascript/src/generated/YGEnums.ts @@ -144,6 +144,7 @@ export enum Unit { MaxContent = 4, FitContent = 5, Stretch = 6, + Dynamic = 7, } export enum Wrap { @@ -238,6 +239,7 @@ const constants = { UNIT_MAX_CONTENT: Unit.MaxContent, UNIT_FIT_CONTENT: Unit.FitContent, UNIT_STRETCH: Unit.Stretch, + UNIT_DYNAMIC: Unit.Dynamic, WRAP_NO_WRAP: Wrap.NoWrap, WRAP_WRAP: Wrap.Wrap, WRAP_WRAP_REVERSE: Wrap.WrapReverse, diff --git a/tests/StyleTest.cpp b/tests/StyleTest.cpp index d44b73d50f..694611b2cf 100644 --- a/tests/StyleTest.cpp +++ b/tests/StyleTest.cpp @@ -14,22 +14,23 @@ TEST(Style, computed_padding_is_floored) { yoga::Style style; style.setPadding(Edge::All, StyleLength::points(-1.0f)); auto paddingStart = style.computeInlineStartPadding( - FlexDirection::Row, Direction::LTR, 0.0f /*widthSize*/); + FlexDirection::Row, Direction::LTR, 0.0f /*widthSize*/, nullptr); ASSERT_EQ(paddingStart, 0.0f); } TEST(Style, computed_border_is_floored) { yoga::Style style; style.setBorder(Edge::All, StyleLength::points(-1.0f)); - auto borderStart = - style.computeInlineStartBorder(FlexDirection::Row, Direction::LTR); + auto borderStart = style.computeInlineStartBorder( + FlexDirection::Row, Direction::LTR, nullptr); ASSERT_EQ(borderStart, 0.0f); } TEST(Style, computed_gap_is_floored) { yoga::Style style; style.setGap(Gutter::Column, StyleLength::points(-1.0f)); - auto gapBetweenColumns = style.computeGapForAxis(FlexDirection::Row, 0.0); + auto gapBetweenColumns = + style.computeGapForAxis(FlexDirection::Row, 0.0, nullptr); ASSERT_EQ(gapBetweenColumns, 0.0f); } @@ -37,7 +38,7 @@ TEST(Style, computed_margin_is_not_floored) { yoga::Style style; style.setMargin(Edge::All, StyleLength::points(-1.0f)); auto marginStart = style.computeInlineStartMargin( - FlexDirection::Row, Direction::LTR, 0.0f /*widthSize*/); + FlexDirection::Row, Direction::LTR, 0.0f /*widthSize*/, nullptr); ASSERT_EQ(marginStart, -1.0f); } diff --git a/yoga/YGEnums.cpp b/yoga/YGEnums.cpp index 538b0b0847..ed83d96201 100644 --- a/yoga/YGEnums.cpp +++ b/yoga/YGEnums.cpp @@ -283,6 +283,8 @@ const char* YGUnitToString(const YGUnit value) { return "fit-content"; case YGUnitStretch: return "stretch"; + case YGUnitDynamic: + return "dynamic"; } return "unknown"; } diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h index aa0b1d4350..54d47c9da6 100644 --- a/yoga/YGEnums.h +++ b/yoga/YGEnums.h @@ -150,7 +150,8 @@ YG_ENUM_DECL( YGUnitAuto, YGUnitMaxContent, YGUnitFitContent, - YGUnitStretch) + YGUnitStretch, + YGUnitDynamic) YG_ENUM_DECL( YGWrap, diff --git a/yoga/YGNodeStyle.cpp b/yoga/YGNodeStyle.cpp index e43a38e64e..0f4866d784 100644 --- a/yoga/YGNodeStyle.cpp +++ b/yoga/YGNodeStyle.cpp @@ -226,6 +226,14 @@ void YGNodeStyleSetFlexBasisStretch(const YGNodeRef node) { node, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetFlexBasisDynamic( + const YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::flexBasis, &Style::setFlexBasis>( + node, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().flexBasis(); } @@ -249,6 +257,15 @@ YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) { return (YGValue)resolveRef(node)->style().position(scopedEnum(edge)); } +void YGNodeStyleSetPositionDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::position, &Style::setPosition>( + node, scopedEnum(edge), StyleLength::dynamic(callback, id)); +} + void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float points) { updateStyle<&Style::margin, &Style::setMargin>( node, scopedEnum(edge), StyleLength::points(points)); @@ -268,6 +285,15 @@ YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) { return (YGValue)resolveRef(node)->style().margin(scopedEnum(edge)); } +void YGNodeStyleSetMarginDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::margin, &Style::setMargin>( + node, scopedEnum(edge), StyleLength::dynamic(callback, id)); +} + void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float points) { updateStyle<&Style::padding, &Style::setPadding>( node, scopedEnum(edge), StyleLength::points(points)); @@ -282,6 +308,15 @@ YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) { return (YGValue)resolveRef(node)->style().padding(scopedEnum(edge)); } +void YGNodeStyleSetPaddingDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::padding, &Style::setPadding>( + node, scopedEnum(edge), StyleLength::dynamic(callback, id)); +} + void YGNodeStyleSetBorder( const YGNodeRef node, const YGEdge edge, @@ -299,6 +334,15 @@ float YGNodeStyleGetBorder(const YGNodeConstRef node, const YGEdge edge) { return static_cast(border).value; } +void YGNodeStyleSetBorderDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::border, &Style::setBorder>( + node, scopedEnum(edge), StyleLength::dynamic(callback, id)); +} + void YGNodeStyleSetGap( const YGNodeRef node, const YGGutter gutter, @@ -312,6 +356,15 @@ void YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float percent) { node, scopedEnum(gutter), StyleLength::percent(percent)); } +void YGNodeStyleSetGapDynamic( + YGNodeRef node, + YGGutter gutter, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::gap, &Style::setGap>( + node, scopedEnum(gutter), StyleLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetGap(const YGNodeConstRef node, const YGGutter gutter) { return (YGValue)resolveRef(node)->style().gap(scopedEnum(gutter)); } @@ -365,6 +418,14 @@ void YGNodeStyleSetWidthStretch(YGNodeRef node) { node, Dimension::Width, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetWidthDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Width, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetWidth(YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().dimension(Dimension::Width); } @@ -399,6 +460,14 @@ void YGNodeStyleSetHeightStretch(YGNodeRef node) { node, Dimension::Height, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetHeightDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::dimension, &Style::setDimension>( + node, Dimension::Height, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetHeight(YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().dimension(Dimension::Height); } @@ -428,6 +497,14 @@ void YGNodeStyleSetMinWidthStretch(const YGNodeRef node) { node, Dimension::Width, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetMinWidthDynamic( + const YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Width, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().minDimension(Dimension::Width); } @@ -459,6 +536,14 @@ void YGNodeStyleSetMinHeightStretch(const YGNodeRef node) { node, Dimension::Height, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetMinHeightDynamic( + const YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::minDimension, &Style::setMinDimension>( + node, Dimension::Height, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().minDimension(Dimension::Height); } @@ -488,6 +573,14 @@ void YGNodeStyleSetMaxWidthStretch(const YGNodeRef node) { node, Dimension::Width, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetMaxWidthDynamic( + const YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Width, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Width); } @@ -519,6 +612,14 @@ void YGNodeStyleSetMaxHeightStretch(const YGNodeRef node) { node, Dimension::Height, StyleSizeLength::ofStretch()); } +void YGNodeStyleSetMaxHeightDynamic( + const YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id) { + updateStyle<&Style::maxDimension, &Style::setMaxDimension>( + node, Dimension::Height, StyleSizeLength::dynamic(callback, id)); +} + YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) { return (YGValue)resolveRef(node)->style().maxDimension(Dimension::Height); } diff --git a/yoga/YGNodeStyle.h b/yoga/YGNodeStyle.h index a1c7e09561..b1785ba65a 100644 --- a/yoga/YGNodeStyle.h +++ b/yoga/YGNodeStyle.h @@ -75,6 +75,10 @@ YG_EXPORT void YGNodeStyleSetFlexBasisAuto(YGNodeRef node); YG_EXPORT void YGNodeStyleSetFlexBasisMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetFlexBasisFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetFlexBasisStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetFlexBasisDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetFlexBasis(YGNodeConstRef node); YG_EXPORT void @@ -83,27 +87,53 @@ YG_EXPORT void YGNodeStyleSetPositionPercent(YGNodeRef node, YGEdge edge, float position); YG_EXPORT YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge); YG_EXPORT void YGNodeStyleSetPositionAuto(YGNodeRef node, YGEdge edge); +YG_EXPORT void YGNodeStyleSetPositionDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float margin); YG_EXPORT void YGNodeStyleSetMarginPercent(YGNodeRef node, YGEdge edge, float margin); YG_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge); +YG_EXPORT void YGNodeStyleSetMarginDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge); YG_EXPORT void YGNodeStyleSetPadding(YGNodeRef node, YGEdge edge, float padding); YG_EXPORT void YGNodeStyleSetPaddingPercent(YGNodeRef node, YGEdge edge, float padding); +YG_EXPORT void YGNodeStyleSetPaddingDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge); YG_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border); +YG_EXPORT void YGNodeStyleSetBorderDynamic( + YGNodeRef node, + YGEdge edge, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge); YG_EXPORT void YGNodeStyleSetGap(YGNodeRef node, YGGutter gutter, float gapLength); YG_EXPORT void YGNodeStyleSetGapPercent(YGNodeRef node, YGGutter gutter, float gapLength); +YG_EXPORT void YGNodeStyleSetGapDynamic( + YGNodeRef node, + YGGutter gutter, + YGValueDynamic callback, + YGValueDynamicID id); + YG_EXPORT YGValue YGNodeStyleGetGap(YGNodeConstRef node, YGGutter gutter); YG_EXPORT void YGNodeStyleSetBoxSizing(YGNodeRef node, YGBoxSizing boxSizing); @@ -115,6 +145,10 @@ YG_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node); YG_EXPORT void YGNodeStyleSetWidthMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetWidthFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetWidthStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetWidthDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetWidth(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetHeight(YGNodeRef node, float height); @@ -123,6 +157,10 @@ YG_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node); YG_EXPORT void YGNodeStyleSetHeightMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetHeightFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetHeightStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetHeightDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetHeight(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetMinWidth(YGNodeRef node, float minWidth); @@ -130,6 +168,10 @@ YG_EXPORT void YGNodeStyleSetMinWidthPercent(YGNodeRef node, float minWidth); YG_EXPORT void YGNodeStyleSetMinWidthMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMinWidthFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMinWidthStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinWidthDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetMinWidth(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetMinHeight(YGNodeRef node, float minHeight); @@ -137,6 +179,10 @@ YG_EXPORT void YGNodeStyleSetMinHeightPercent(YGNodeRef node, float minHeight); YG_EXPORT void YGNodeStyleSetMinHeightMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMinHeightFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMinHeightStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMinHeightDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetMinHeight(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetMaxWidth(YGNodeRef node, float maxWidth); @@ -144,6 +190,10 @@ YG_EXPORT void YGNodeStyleSetMaxWidthPercent(YGNodeRef node, float maxWidth); YG_EXPORT void YGNodeStyleSetMaxWidthMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMaxWidthFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMaxWidthStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxWidthDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetMaxWidth(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetMaxHeight(YGNodeRef node, float maxHeight); @@ -151,6 +201,10 @@ YG_EXPORT void YGNodeStyleSetMaxHeightPercent(YGNodeRef node, float maxHeight); YG_EXPORT void YGNodeStyleSetMaxHeightMaxContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMaxHeightFitContent(YGNodeRef node); YG_EXPORT void YGNodeStyleSetMaxHeightStretch(YGNodeRef node); +YG_EXPORT void YGNodeStyleSetMaxHeightDynamic( + YGNodeRef node, + YGValueDynamic callback, + YGValueDynamicID id); YG_EXPORT YGValue YGNodeStyleGetMaxHeight(YGNodeConstRef node); YG_EXPORT void YGNodeStyleSetAspectRatio(YGNodeRef node, float aspectRatio); diff --git a/yoga/YGValue.h b/yoga/YGValue.h index 138135229b..5666880626 100644 --- a/yoga/YGValue.h +++ b/yoga/YGValue.h @@ -25,6 +25,8 @@ constexpr float YGUndefined = std::numeric_limits::quiet_NaN(); YG_EXTERN_C_BEGIN +typedef const struct YGNode* YGNodeConstRef; + /** * Structure used to represent a dimension in a style. */ @@ -53,6 +55,36 @@ YG_EXPORT extern const YGValue YGValueZero; */ YG_EXPORT bool YGFloatIsUndefined(float value); +/** + * Host-defined identifier for a dynamic style value. + */ +typedef uint8_t YGValueDynamicID; + +/** + * Layout context passed to YGValueDynamic for resolving dynamic values. + * May be extended with additional fields (e.g. containing block dimensions). + */ +typedef struct YGValueDynamicContext { + float referenceLength; +} YGValueDynamicContext; + +/** + * Called during layout to resolve a dynamic style value (e.g. calc()). + * Must return a YGValue with unit YGUnitPoint. + */ +typedef YGValue (*YGValueDynamic)( + YGNodeConstRef node, + YGValueDynamicID id, + YGValueDynamicContext context); + +/** + * Callback + identifier pair for internal storage. + */ +struct YGValueDynamicData { + YGValueDynamic callback; + YGValueDynamicID id; +}; + YG_EXTERN_C_END // Equality operators for comparison of YGValue in C++ @@ -72,6 +104,7 @@ inline bool operator==(const YGValue& lhs, const YGValue& rhs) { case YGUnitPoint: case YGUnitPercent: return lhs.value == rhs.value; + case YGUnitDynamic: default: return false; } diff --git a/yoga/algorithm/AbsoluteLayout.cpp b/yoga/algorithm/AbsoluteLayout.cpp index 181dfcb1b0..d4ce6967df 100644 --- a/yoga/algorithm/AbsoluteLayout.cpp +++ b/yoga/algorithm/AbsoluteLayout.cpp @@ -20,7 +20,7 @@ static inline void setFlexStartLayoutPosition( const FlexDirection axis, const float containingBlockWidth) { float position = child->style().computeFlexStartMargin( - axis, direction, containingBlockWidth) + + axis, direction, containingBlockWidth, child) + parent->getLayout().border(flexStartEdge(axis)); // https://www.w3.org/TR/css-grid-1/#abspos @@ -42,7 +42,7 @@ static inline void setFlexEndLayoutPosition( const float containingBlockWidth) { float flexEndPosition = parent->getLayout().border(flexEndEdge(axis)) + child->style().computeFlexEndMargin( - axis, direction, containingBlockWidth); + axis, direction, containingBlockWidth, child); // https://www.w3.org/TR/css-grid-1/#abspos // absolute positioned grid items are positioned relative to the padding edge @@ -79,12 +79,12 @@ static inline void setCenterLayoutPosition( const float childOuterSize = child->getLayout().measuredDimension(dimension(axis)) + - child->style().computeMarginForAxis(axis, containingBlockWidth); + child->style().computeMarginForAxis(axis, containingBlockWidth, child); float position = (parentContentBoxSize - childOuterSize) / 2.0f + parent->getLayout().border(flexStartEdge(axis)) + child->style().computeFlexStartMargin( - axis, direction, containingBlockWidth); + axis, direction, containingBlockWidth, child); // https://www.w3.org/TR/css-grid-1/#abspos // absolute positioned grid items are positioned relative to the padding edge @@ -210,10 +210,11 @@ static void positionAbsoluteChild( !child->style().isInlineStartPositionAuto(axis, direction)) { const float positionRelativeToInlineStart = child->style().computeInlineStartPosition( - axis, direction, containingBlockSize) + - containingNode->style().computeInlineStartBorder(axis, direction) + + axis, direction, containingBlockSize, child) + + containingNode->style().computeInlineStartBorder( + axis, direction, containingNode) + child->style().computeInlineStartMargin( - axis, direction, containingBlockSize); + axis, direction, containingBlockSize, child); const float positionRelativeToFlexStart = inlineStartEdge(axis, direction) != flexStartEdge(axis) ? getPositionOfOppositeEdge( @@ -227,11 +228,12 @@ static void positionAbsoluteChild( const float positionRelativeToInlineStart = containingNode->getLayout().measuredDimension(dimension(axis)) - child->getLayout().measuredDimension(dimension(axis)) - - containingNode->style().computeInlineEndBorder(axis, direction) - + containingNode->style().computeInlineEndBorder( + axis, direction, containingNode) - child->style().computeInlineEndMargin( - axis, direction, containingBlockSize) - + axis, direction, containingBlockSize, child) - child->style().computeInlineEndPosition( - axis, direction, containingBlockSize); + axis, direction, containingBlockSize, child); const float positionRelativeToFlexStart = inlineStartEdge(axis, direction) != flexStartEdge(axis) ? getPositionOfOppositeEdge( @@ -275,9 +277,9 @@ void layoutAbsoluteChild( SizingMode childHeightSizingMode = SizingMode::MaxContent; auto marginRow = child->style().computeMarginForAxis( - FlexDirection::Row, containingBlockWidth); + FlexDirection::Row, containingBlockWidth, child); auto marginColumn = child->style().computeMarginForAxis( - FlexDirection::Column, containingBlockWidth); + FlexDirection::Column, containingBlockWidth, child); if (child->hasDefiniteLength(Dimension::Width, containingBlockWidth)) { childWidth = child @@ -301,13 +303,13 @@ void layoutAbsoluteChild( childWidth = containingNode->getLayout().measuredDimension(Dimension::Width) - (containingNode->style().computeFlexStartBorder( - FlexDirection::Row, direction) + + FlexDirection::Row, direction, containingNode) + containingNode->style().computeFlexEndBorder( - FlexDirection::Row, direction)) - + FlexDirection::Row, direction, containingNode)) - (child->style().computeFlexStartPosition( - FlexDirection::Row, direction, containingBlockWidth) + + FlexDirection::Row, direction, containingBlockWidth, child) + child->style().computeFlexEndPosition( - FlexDirection::Row, direction, containingBlockWidth)); + FlexDirection::Row, direction, containingBlockWidth, child)); childWidth = boundAxis( child, FlexDirection::Row, @@ -341,13 +343,13 @@ void layoutAbsoluteChild( childHeight = containingNode->getLayout().measuredDimension(Dimension::Height) - (containingNode->style().computeFlexStartBorder( - FlexDirection::Column, direction) + + FlexDirection::Column, direction, containingNode) + containingNode->style().computeFlexEndBorder( - FlexDirection::Column, direction)) - + FlexDirection::Column, direction, containingNode)) - (child->style().computeFlexStartPosition( - FlexDirection::Column, direction, containingBlockHeight) + + FlexDirection::Column, direction, containingBlockHeight, child) + child->style().computeFlexEndPosition( - FlexDirection::Column, direction, containingBlockHeight)); + FlexDirection::Column, direction, containingBlockHeight, child)); childHeight = boundAxis( child, FlexDirection::Column, @@ -410,10 +412,10 @@ void layoutAbsoluteChild( generationCount); childWidth = child->getLayout().measuredDimension(Dimension::Width) + child->style().computeMarginForAxis( - FlexDirection::Row, containingBlockWidth); + FlexDirection::Row, containingBlockWidth, child); childHeight = child->getLayout().measuredDimension(Dimension::Height) + child->style().computeMarginForAxis( - FlexDirection::Column, containingBlockWidth); + FlexDirection::Column, containingBlockWidth, child); } calculateLayoutInternal( @@ -473,12 +475,13 @@ bool layoutAbsoluteDescendants( const float containingBlockWidth = absoluteErrata ? containingNodeAvailableInnerWidth : containingNode->getLayout().measuredDimension(Dimension::Width) - - containingNode->style().computeBorderForAxis(FlexDirection::Row); + containingNode->style().computeBorderForAxis( + FlexDirection::Row, containingNode); const float containingBlockHeight = absoluteErrata ? containingNodeAvailableInnerHeight : containingNode->getLayout().measuredDimension(Dimension::Height) - containingNode->style().computeBorderForAxis( - FlexDirection::Column); + FlexDirection::Column, containingNode); layoutAbsoluteChild( containingNode, diff --git a/yoga/algorithm/BoundAxis.h b/yoga/algorithm/BoundAxis.h index d9ebc68c5b..f90b1ad2bc 100644 --- a/yoga/algorithm/BoundAxis.h +++ b/yoga/algorithm/BoundAxis.h @@ -22,9 +22,9 @@ inline float paddingAndBorderForAxis( const Direction direction, const float widthSize) { return node->style().computeInlineStartPaddingAndBorder( - axis, direction, widthSize) + + axis, direction, widthSize, node) + node->style().computeInlineEndPaddingAndBorder( - axis, direction, widthSize); + axis, direction, widthSize, node); } inline FloatOptional boundAxisWithinMinAndMax( @@ -39,14 +39,14 @@ inline FloatOptional boundAxisWithinMinAndMax( if (isColumn(axis)) { min = node->style().resolvedMinDimension( - direction, Dimension::Height, axisSize, widthSize); + direction, Dimension::Height, axisSize, widthSize, node); max = node->style().resolvedMaxDimension( - direction, Dimension::Height, axisSize, widthSize); + direction, Dimension::Height, axisSize, widthSize, node); } else if (isRow(axis)) { min = node->style().resolvedMinDimension( - direction, Dimension::Width, axisSize, widthSize); + direction, Dimension::Width, axisSize, widthSize, node); max = node->style().resolvedMaxDimension( - direction, Dimension::Width, axisSize, widthSize); + direction, Dimension::Width, axisSize, widthSize, node); } if (max >= FloatOptional{0} && value > max) { diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp index 6c90ddce39..da089b848f 100644 --- a/yoga/algorithm/CalculateLayout.cpp +++ b/yoga/algorithm/CalculateLayout.cpp @@ -45,8 +45,8 @@ void constrainMaxSizeForMode( /*in_out*/ float* size) { const FloatOptional maxSize = node->style().resolvedMaxDimension( - direction, dimension(axis), ownerAxisSize, ownerWidth) + - FloatOptional(node->style().computeMarginForAxis(axis, ownerWidth)); + direction, dimension(axis), ownerAxisSize, ownerWidth, node) + + FloatOptional(node->style().computeMarginForAxis(axis, ownerWidth, node)); switch (*mode) { case SizingMode::StretchFit: case SizingMode::FitContent: @@ -142,10 +142,10 @@ static void computeFlexBasisForChild( childWidthSizingMode = SizingMode::MaxContent; childHeightSizingMode = SizingMode::MaxContent; - auto marginRow = - child->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); - auto marginColumn = - child->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + auto marginRow = child->style().computeMarginForAxis( + FlexDirection::Row, ownerWidth, child); + auto marginColumn = child->style().computeMarginForAxis( + FlexDirection::Column, ownerWidth, child); if (isRowStyleDimDefined) { childWidth = child @@ -540,14 +540,14 @@ float calculateAvailableInnerDimension( // constraints const FloatOptional minDimensionOptional = node->style().resolvedMinDimension( - direction, dimension, ownerDim, ownerWidth); + direction, dimension, ownerDim, ownerWidth, node); const float minInnerDim = minDimensionOptional.isUndefined() ? 0.0f : minDimensionOptional.unwrap() - paddingAndBorder; const FloatOptional maxDimensionOptional = node->style().resolvedMaxDimension( - direction, dimension, ownerDim, ownerWidth); + direction, dimension, ownerDim, ownerWidth, node); const float maxInnerDim = maxDimensionOptional.isUndefined() ? FLT_MAX @@ -637,7 +637,8 @@ static float computeFlexBasisForChildren( totalOuterFlexBasis += (child->getLayout().computedFlexBasis.unwrap() + - child->style().computeMarginForAxis(mainAxis, availableInnerWidth)); + child->style().computeMarginForAxis( + mainAxis, availableInnerWidth, child)); } return totalOuterFlexBasis; @@ -731,9 +732,9 @@ static float distributeFreeSpaceSecondPass( deltaFreeSpace += updatedMainSize - childFlexBasis; const float marginMain = currentLineChild->style().computeMarginForAxis( - mainAxis, availableInnerWidth); + mainAxis, availableInnerWidth, currentLineChild); const float marginCross = currentLineChild->style().computeMarginForAxis( - crossAxis, availableInnerWidth); + crossAxis, availableInnerWidth, currentLineChild); float childCrossSize = YGUndefined; float childMainSize = updatedMainSize + marginMain; @@ -1025,13 +1026,13 @@ static void justifyMainAxis( const float leadingPaddingAndBorderMain = node->style().computeFlexStartPaddingAndBorder( - mainAxis, direction, ownerWidth); + mainAxis, direction, ownerWidth, node); const float trailingPaddingAndBorderMain = node->style().computeFlexEndPaddingAndBorder( - mainAxis, direction, ownerWidth); + mainAxis, direction, ownerWidth, node); const float gap = - node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + node->style().computeGapForAxis(mainAxis, availableInnerMainDim, node); // If we are using "at most" rules in the main axis, make sure that // remainingFreeSpace is 0 when min main dimension is not given if (sizingModeMainDim == SizingMode::FitContent && @@ -1039,7 +1040,11 @@ static void justifyMainAxis( if (style.minDimension(dimension(mainAxis)).isDefined() && style .resolvedMinDimension( - direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth) + direction, + dimension(mainAxis), + mainAxisOwnerSize, + ownerWidth, + node) .isDefined()) { // This condition makes sure that if the size of main dimension(after // considering child nodes main dim, leading and trailing padding etc) @@ -1048,11 +1053,14 @@ static void justifyMainAxis( // `minAvailableMainDim` denotes minimum available space in which child // can be laid out, it will exclude space consumed by padding and border. - const float minAvailableMainDim = - style - .resolvedMinDimension( - direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth) - .unwrap() - + const float minAvailableMainDim = style + .resolvedMinDimension( + direction, + dimension(mainAxis), + mainAxisOwnerSize, + ownerWidth, + node) + .unwrap() - leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; const float occupiedSpaceByChildNodes = availableInnerMainDim - flexLine.layout.remainingFreeSpace; @@ -1147,8 +1155,8 @@ static void justifyMainAxis( // If we skipped the flex step, then we can't rely on the measuredDims // because they weren't computed. This means we can't call // dimensionWithMargin. - flexLine.layout.mainDim += - child->style().computeMarginForAxis(mainAxis, availableInnerWidth) + + flexLine.layout.mainDim += child->style().computeMarginForAxis( + mainAxis, availableInnerWidth, child) + boundAxisWithinMinAndMax( child, direction, @@ -1169,11 +1177,11 @@ static void justifyMainAxis( // calculated by adding maxAscent and maxDescent from the baseline. const float ascent = calculateBaseline(child) + child->style().computeFlexStartMargin( - FlexDirection::Column, direction, availableInnerWidth); + FlexDirection::Column, direction, availableInnerWidth, child); const float descent = child->getLayout().measuredDimension(Dimension::Height) + child->style().computeMarginForAxis( - FlexDirection::Column, availableInnerWidth) - + FlexDirection::Column, availableInnerWidth, child) - ascent; maxAscentForCurrentLine = @@ -1300,49 +1308,51 @@ static void calculateLayoutImpl( direction == Direction::LTR ? PhysicalEdge::Right : PhysicalEdge::Left; const float marginRowLeading = node->style().computeInlineStartMargin( - flexRowDirection, direction, ownerWidth); + flexRowDirection, direction, ownerWidth, node); node->setLayoutMargin(marginRowLeading, startEdge); const float marginRowTrailing = node->style().computeInlineEndMargin( - flexRowDirection, direction, ownerWidth); + flexRowDirection, direction, ownerWidth, node); node->setLayoutMargin(marginRowTrailing, endEdge); const float marginColumnLeading = node->style().computeInlineStartMargin( - flexColumnDirection, direction, ownerWidth); + flexColumnDirection, direction, ownerWidth, node); node->setLayoutMargin(marginColumnLeading, PhysicalEdge::Top); const float marginColumnTrailing = node->style().computeInlineEndMargin( - flexColumnDirection, direction, ownerWidth); + flexColumnDirection, direction, ownerWidth, node); node->setLayoutMargin(marginColumnTrailing, PhysicalEdge::Bottom); const float marginAxisRow = marginRowLeading + marginRowTrailing; const float marginAxisColumn = marginColumnLeading + marginColumnTrailing; node->setLayoutBorder( - node->style().computeInlineStartBorder(flexRowDirection, direction), + node->style().computeInlineStartBorder(flexRowDirection, direction, node), startEdge); node->setLayoutBorder( - node->style().computeInlineEndBorder(flexRowDirection, direction), + node->style().computeInlineEndBorder(flexRowDirection, direction, node), endEdge); node->setLayoutBorder( - node->style().computeInlineStartBorder(flexColumnDirection, direction), + node->style().computeInlineStartBorder( + flexColumnDirection, direction, node), PhysicalEdge::Top); node->setLayoutBorder( - node->style().computeInlineEndBorder(flexColumnDirection, direction), + node->style().computeInlineEndBorder( + flexColumnDirection, direction, node), PhysicalEdge::Bottom); node->setLayoutPadding( node->style().computeInlineStartPadding( - flexRowDirection, direction, ownerWidth), + flexRowDirection, direction, ownerWidth, node), startEdge); node->setLayoutPadding( node->style().computeInlineEndPadding( - flexRowDirection, direction, ownerWidth), + flexRowDirection, direction, ownerWidth, node), endEdge); node->setLayoutPadding( node->style().computeInlineStartPadding( - flexColumnDirection, direction, ownerWidth), + flexColumnDirection, direction, ownerWidth, node), PhysicalEdge::Top); node->setLayoutPadding( node->style().computeInlineEndPadding( - flexColumnDirection, direction, ownerWidth), + flexColumnDirection, direction, ownerWidth, node), PhysicalEdge::Bottom); if (node->hasMeasureFunc()) { @@ -1426,7 +1436,7 @@ static void calculateLayoutImpl( paddingAndBorderForAxis(node, crossAxis, direction, ownerWidth); const float leadingPaddingAndBorderCross = node->style().computeFlexStartPaddingAndBorder( - crossAxis, direction, ownerWidth); + crossAxis, direction, ownerWidth, node); SizingMode sizingModeMainDim = isMainAxisRow ? widthSizingMode : heightSizingMode; @@ -1522,7 +1532,7 @@ static void calculateLayoutImpl( if (childCount > 1) { totalMainDim += - node->style().computeGapForAxis(mainAxis, availableInnerMainDim) * + node->style().computeGapForAxis(mainAxis, availableInnerMainDim, node) * static_cast(childCount - 1); } @@ -1547,7 +1557,7 @@ static void calculateLayoutImpl( float totalLineCrossDim = 0; const float crossAxisGap = - node->style().computeGapForAxis(crossAxis, availableInnerCrossDim); + node->style().computeGapForAxis(crossAxis, availableInnerCrossDim, node); // Max main dimension of all the lines. float maxLineMainDim = 0; @@ -1580,25 +1590,25 @@ static void calculateLayoutImpl( const float minInnerWidth = style .resolvedMinDimension( - direction, Dimension::Width, ownerWidth, ownerWidth) + direction, Dimension::Width, ownerWidth, ownerWidth, node) .unwrap() - paddingAndBorderAxisRow; const float maxInnerWidth = style .resolvedMaxDimension( - direction, Dimension::Width, ownerWidth, ownerWidth) + direction, Dimension::Width, ownerWidth, ownerWidth, node) .unwrap() - paddingAndBorderAxisRow; const float minInnerHeight = style .resolvedMinDimension( - direction, Dimension::Height, ownerHeight, ownerWidth) + direction, Dimension::Height, ownerHeight, ownerWidth, node) .unwrap() - paddingAndBorderAxisColumn; const float maxInnerHeight = style .resolvedMaxDimension( - direction, Dimension::Height, ownerHeight, ownerWidth) + direction, Dimension::Height, ownerHeight, ownerWidth, node) .unwrap() - paddingAndBorderAxisColumn; @@ -1753,14 +1763,14 @@ static void calculateLayoutImpl( const auto& childStyle = child->style(); float childCrossSize = childStyle.aspectRatio().isDefined() ? child->style().computeMarginForAxis( - crossAxis, availableInnerWidth) + + crossAxis, availableInnerWidth, child) + (isMainAxisRow ? childMainSize / childStyle.aspectRatio().unwrap() : childMainSize * childStyle.aspectRatio().unwrap()) : flexLine.layout.crossDim; childMainSize += child->style().computeMarginForAxis( - mainAxis, availableInnerWidth); + mainAxis, availableInnerWidth, child); SizingMode childMainSizingMode = SizingMode::StretchFit; SizingMode childCrossSizingMode = SizingMode::StretchFit; @@ -1824,8 +1834,8 @@ static void calculateLayoutImpl( leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim / 2); } else if (child->style().flexEndMarginIsAuto(crossAxis, direction)) { // No-Op - } else if (child->style().flexStartMarginIsAuto( - crossAxis, direction)) { + } else if ( + child->style().flexStartMarginIsAuto(crossAxis, direction)) { leadingCrossDim += yoga::maxOrDefined(0.0f, remainingCrossDim); } else if (alignItem == Align::FlexStart) { // No-Op @@ -1943,16 +1953,19 @@ static void calculateLayoutImpl( lineHeight, child->getLayout().measuredDimension(dimension(crossAxis)) + child->style().computeMarginForAxis( - crossAxis, availableInnerWidth)); + crossAxis, availableInnerWidth, child)); } if (resolveChildAlignment(node, child) == Align::Baseline) { const float ascent = calculateBaseline(child) + child->style().computeFlexStartMargin( - FlexDirection::Column, direction, availableInnerWidth); + FlexDirection::Column, + direction, + availableInnerWidth, + child); const float descent = child->getLayout().measuredDimension(Dimension::Height) + child->style().computeMarginForAxis( - FlexDirection::Column, availableInnerWidth) - + FlexDirection::Column, availableInnerWidth, child) - ascent; maxAscentForCurrentLine = yoga::maxOrDefined(maxAscentForCurrentLine, ascent); @@ -1982,7 +1995,7 @@ static void calculateLayoutImpl( child->setLayoutPosition( currentLead + child->style().computeFlexStartPosition( - crossAxis, direction, availableInnerWidth), + crossAxis, direction, availableInnerWidth, child), flexStartEdge(crossAxis)); break; } @@ -1990,7 +2003,7 @@ static void calculateLayoutImpl( child->setLayoutPosition( currentLead + lineHeight - child->style().computeFlexEndMargin( - crossAxis, direction, availableInnerWidth) - + crossAxis, direction, availableInnerWidth, child) - child->getLayout().measuredDimension( dimension(crossAxis)), flexStartEdge(crossAxis)); @@ -2009,7 +2022,7 @@ static void calculateLayoutImpl( child->setLayoutPosition( currentLead + child->style().computeFlexStartMargin( - crossAxis, direction, availableInnerWidth), + crossAxis, direction, availableInnerWidth, child), flexStartEdge(crossAxis)); // Remeasure child with the line height as it as been only @@ -2019,13 +2032,13 @@ static void calculateLayoutImpl( const float childWidth = isMainAxisRow ? (child->getLayout().measuredDimension(Dimension::Width) + child->style().computeMarginForAxis( - mainAxis, availableInnerWidth)) + mainAxis, availableInnerWidth, child)) : leadPerLine + lineHeight; const float childHeight = !isMainAxisRow ? (child->getLayout().measuredDimension(Dimension::Height) + child->style().computeMarginForAxis( - crossAxis, availableInnerWidth)) + crossAxis, availableInnerWidth, child)) : leadPerLine + lineHeight; if (!(yoga::inexactEquals( @@ -2061,7 +2074,8 @@ static void calculateLayoutImpl( child->style().computeFlexStartPosition( FlexDirection::Column, direction, - availableInnerCrossDim), + availableInnerCrossDim, + child), PhysicalEdge::Top); break; @@ -2283,10 +2297,10 @@ bool calculateLayoutInternal( // they are the most expensive to measure, so it's worth avoiding redundant // measurements if at all possible. if (node->hasMeasureFunc()) { - const float marginAxisRow = - node->style().computeMarginForAxis(FlexDirection::Row, ownerWidth); - const float marginAxisColumn = - node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth); + const float marginAxisRow = node->style().computeMarginForAxis( + FlexDirection::Row, ownerWidth, node); + const float marginAxisColumn = node->style().computeMarginForAxis( + FlexDirection::Column, ownerWidth, node); // First, try to use the layout cache. if (canUseCachedMeasurement( @@ -2461,15 +2475,17 @@ void calculateLayout( ownerWidth, ownerWidth) .unwrap() + - node->style().computeMarginForAxis(FlexDirection::Row, ownerWidth)); + node->style().computeMarginForAxis( + FlexDirection::Row, ownerWidth, node)); widthSizingMode = SizingMode::StretchFit; - } else if (style - .resolvedMaxDimension( - direction, Dimension::Width, ownerWidth, ownerWidth) - .isDefined()) { + } else if ( + style + .resolvedMaxDimension( + direction, Dimension::Width, ownerWidth, ownerWidth, node) + .isDefined()) { width = style .resolvedMaxDimension( - direction, Dimension::Width, ownerWidth, ownerWidth) + direction, Dimension::Width, ownerWidth, ownerWidth, node) .unwrap(); widthSizingMode = SizingMode::FitContent; } else { @@ -2488,16 +2504,19 @@ void calculateLayout( ownerHeight, ownerWidth) .unwrap() + - node->style().computeMarginForAxis(FlexDirection::Column, ownerWidth)); + node->style().computeMarginForAxis( + FlexDirection::Column, ownerWidth, node)); heightSizingMode = SizingMode::StretchFit; - } else if (style - .resolvedMaxDimension( - direction, Dimension::Height, ownerHeight, ownerWidth) - .isDefined()) { - height = style - .resolvedMaxDimension( - direction, Dimension::Height, ownerHeight, ownerWidth) - .unwrap(); + } else if ( + style + .resolvedMaxDimension( + direction, Dimension::Height, ownerHeight, ownerWidth, node) + .isDefined()) { + height = + style + .resolvedMaxDimension( + direction, Dimension::Height, ownerHeight, ownerWidth, node) + .unwrap(); heightSizingMode = SizingMode::FitContent; } else { height = ownerHeight; diff --git a/yoga/algorithm/FlexLine.cpp b/yoga/algorithm/FlexLine.cpp index dc0a300add..0f6e2e2c9b 100644 --- a/yoga/algorithm/FlexLine.cpp +++ b/yoga/algorithm/FlexLine.cpp @@ -37,7 +37,7 @@ FlexLine calculateFlexLine( resolveDirection(node->style().flexDirection(), direction); const bool isNodeFlexWrap = node->style().flexWrap() != Wrap::NoWrap; const float gap = - node->style().computeGapForAxis(mainAxis, availableInnerMainDim); + node->style().computeGapForAxis(mainAxis, availableInnerMainDim, node); const auto childrenEnd = node->getLayoutChildren().end(); // Add items to the current line until it's full or we run out of items. @@ -60,8 +60,8 @@ FlexLine calculateFlexLine( } child->setLineIndex(lineCount); - const float childMarginMainAxis = - child->style().computeMarginForAxis(mainAxis, availableInnerWidth); + const float childMarginMainAxis = child->style().computeMarginForAxis( + mainAxis, availableInnerWidth, child); const float childLeadingGapMainAxis = child == firstElementInLine ? 0.0f : gap; const float flexBasisWithMinAndMaxConstraints = diff --git a/yoga/enums/Unit.h b/yoga/enums/Unit.h index 685b1caece..9176ad21e5 100644 --- a/yoga/enums/Unit.h +++ b/yoga/enums/Unit.h @@ -23,11 +23,12 @@ enum class Unit : uint8_t { MaxContent = YGUnitMaxContent, FitContent = YGUnitFitContent, Stretch = YGUnitStretch, + Dynamic = YGUnitDynamic, }; template <> constexpr int32_t ordinalCount() { - return 7; + return 8; } constexpr Unit scopedEnum(YGUnit unscoped) { diff --git a/yoga/node/Node.cpp b/yoga/node/Node.cpp index d42bd5f9e7..e98eda8d05 100644 --- a/yoga/node/Node.cpp +++ b/yoga/node/Node.cpp @@ -87,7 +87,7 @@ float Node::dimensionWithMargin( const FlexDirection axis, const float widthSize) { return getLayout().measuredDimension(dimension(axis)) + - style_.computeMarginForAxis(axis, widthSize); + style_.computeMarginForAxis(axis, widthSize, this); } bool Node::isLayoutDimensionDefined(const FlexDirection axis) { @@ -273,10 +273,10 @@ float Node::relativePosition( } if (style_.isInlineStartPositionDefined(axis, direction) && !style_.isInlineStartPositionAuto(axis, direction)) { - return style_.computeInlineStartPosition(axis, direction, axisSize); + return style_.computeInlineStartPosition(axis, direction, axisSize, this); } - return -1 * style_.computeInlineEndPosition(axis, direction, axisSize); + return -1 * style_.computeInlineEndPosition(axis, direction, axisSize, this); } void Node::setPosition( @@ -309,19 +309,19 @@ void Node::setPosition( const auto crossAxisTrailingEdge = inlineEndEdge(crossAxis, direction); setLayoutPosition( - (style_.computeInlineStartMargin(mainAxis, direction, ownerWidth) + + (style_.computeInlineStartMargin(mainAxis, direction, ownerWidth, this) + relativePositionMain), mainAxisLeadingEdge); setLayoutPosition( - (style_.computeInlineEndMargin(mainAxis, direction, ownerWidth) + + (style_.computeInlineEndMargin(mainAxis, direction, ownerWidth, this) + relativePositionMain), mainAxisTrailingEdge); setLayoutPosition( - (style_.computeInlineStartMargin(crossAxis, direction, ownerWidth) + + (style_.computeInlineStartMargin(crossAxis, direction, ownerWidth, this) + relativePositionCross), crossAxisLeadingEdge); setLayoutPosition( - (style_.computeInlineEndMargin(crossAxis, direction, ownerWidth) + + (style_.computeInlineEndMargin(crossAxis, direction, ownerWidth, this) + relativePositionCross), crossAxisTrailingEdge); } @@ -343,14 +343,15 @@ FloatOptional Node::resolveFlexBasis( FlexDirection flexDirection, float referenceLength, float ownerWidth) const { - FloatOptional value = processFlexBasis().resolve(referenceLength); + FloatOptional value = processFlexBasis().resolve(referenceLength, this); if (style_.boxSizing() == BoxSizing::BorderBox) { return value; } Dimension dim = dimension(flexDirection); - FloatOptional dimensionPaddingAndBorder = FloatOptional{ - style_.computePaddingAndBorderForDimension(direction, dim, ownerWidth)}; + FloatOptional dimensionPaddingAndBorder = + FloatOptional{style_.computePaddingAndBorderForDimension( + direction, dim, ownerWidth, this)}; return value + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder diff --git a/yoga/node/Node.h b/yoga/node/Node.h index d22c4c1ef0..caf076a7c8 100644 --- a/yoga/node/Node.h +++ b/yoga/node/Node.h @@ -88,7 +88,7 @@ class YG_EXPORT Node : public ::YGNode { * https://www.w3.org/TR/css-sizing-3/#definite */ inline bool hasDefiniteLength(Dimension dimension, float ownerSize) { - auto usedValue = getProcessedDimension(dimension).resolve(ownerSize); + auto usedValue = getProcessedDimension(dimension).resolve(ownerSize, this); return usedValue.isDefined() && usedValue.unwrap() >= 0.0f; } @@ -186,14 +186,14 @@ class YG_EXPORT Node : public ::YGNode { float referenceLength, float ownerWidth) const { FloatOptional value = - getProcessedDimension(dimension).resolve(referenceLength); + getProcessedDimension(dimension).resolve(referenceLength, this); if (style_.boxSizing() == BoxSizing::BorderBox) { return value; } FloatOptional dimensionPaddingAndBorder = FloatOptional{style_.computePaddingAndBorderForDimension( - direction, dimension, ownerWidth)}; + direction, dimension, ownerWidth, this)}; return value + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder diff --git a/yoga/style/Style.h b/yoga/style/Style.h index 37c0ba2a9a..556b28d71e 100644 --- a/yoga/style/Style.h +++ b/yoga/style/Style.h @@ -293,18 +293,19 @@ class YG_EXPORT Style { Direction direction, Dimension axis, float referenceLength, - float ownerWidth) const { + float ownerWidth, + YGNodeConstRef node) const { const auto handle = minDimensions_[yoga::to_underlying(axis)]; if (handle.isUndefined()) { return FloatOptional{}; } - FloatOptional value = resolve(handle, referenceLength); + FloatOptional value = resolve(handle, referenceLength, node); if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) { return value; } FloatOptional dimensionPaddingAndBorder = FloatOptional{ - computePaddingAndBorderForDimension(direction, axis, ownerWidth)}; + computePaddingAndBorderForDimension(direction, axis, ownerWidth, node)}; return value + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder @@ -322,18 +323,19 @@ class YG_EXPORT Style { Direction direction, Dimension axis, float referenceLength, - float ownerWidth) const { + float ownerWidth, + YGNodeConstRef node) const { const auto handle = maxDimensions_[yoga::to_underlying(axis)]; if (handle.isUndefined()) { return FloatOptional{}; } - FloatOptional value = resolve(handle, referenceLength); + FloatOptional value = resolve(handle, referenceLength, node); if (boxSizing() == BoxSizing::BorderBox || !value.isDefined()) { return value; } FloatOptional dimensionPaddingAndBorder = FloatOptional{ - computePaddingAndBorderForDimension(direction, axis, ownerWidth)}; + computePaddingAndBorderForDimension(direction, axis, ownerWidth, node)}; return value + (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder @@ -416,99 +418,132 @@ class YG_EXPORT Style { float computeFlexStartPosition( FlexDirection axis, Direction direction, - float axisSize) const { - return resolve(computePosition(flexStartEdge(axis), direction), axisSize) + float axisSize, + YGNodeConstRef node) const { + return resolve( + computePosition(flexStartEdge(axis), direction), axisSize, node) .unwrapOrDefault(0.0f); } float computeInlineStartPosition( FlexDirection axis, Direction direction, - float axisSize) const { + float axisSize, + YGNodeConstRef node) const { return resolve( computePosition(inlineStartEdge(axis, direction), direction), - axisSize) + axisSize, + node) .unwrapOrDefault(0.0f); } float computeFlexEndPosition( FlexDirection axis, Direction direction, - float axisSize) const { - return resolve(computePosition(flexEndEdge(axis), direction), axisSize) + float axisSize, + YGNodeConstRef node) const { + return resolve( + computePosition(flexEndEdge(axis), direction), axisSize, node) .unwrapOrDefault(0.0f); } float computeInlineEndPosition( FlexDirection axis, Direction direction, - float axisSize) const { + float axisSize, + YGNodeConstRef node) const { return resolve( computePosition(inlineEndEdge(axis, direction), direction), - axisSize) + axisSize, + node) .unwrapOrDefault(0.0f); } float computeFlexStartMargin( FlexDirection axis, Direction direction, - float widthSize) const { - return resolve(computeMargin(flexStartEdge(axis), direction), widthSize) + float widthSize, + YGNodeConstRef node) const { + return resolve( + computeMargin(flexStartEdge(axis), direction), widthSize, node) .unwrapOrDefault(0.0f); } float computeInlineStartMargin( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return resolve( computeMargin(inlineStartEdge(axis, direction), direction), - widthSize) + widthSize, + node) .unwrapOrDefault(0.0f); } float computeFlexEndMargin( FlexDirection axis, Direction direction, - float widthSize) const { - return resolve(computeMargin(flexEndEdge(axis), direction), widthSize) + float widthSize, + YGNodeConstRef node) const { + return resolve(computeMargin(flexEndEdge(axis), direction), widthSize, node) .unwrapOrDefault(0.0f); } float computeInlineEndMargin( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return resolve( computeMargin(inlineEndEdge(axis, direction), direction), - widthSize) + widthSize, + node) .unwrapOrDefault(0.0f); } - float computeFlexStartBorder(FlexDirection axis, Direction direction) const { + float computeFlexStartBorder( + FlexDirection axis, + Direction direction, + YGNodeConstRef node) const { return maxOrDefined( - resolve(computeBorder(flexStartEdge(axis), direction), 0.0f).unwrap(), + resolve(computeBorder(flexStartEdge(axis), direction), 0.0f, node) + .unwrap(), 0.0f); } - float computeInlineStartBorder(FlexDirection axis, Direction direction) - const { + float computeInlineStartBorder( + FlexDirection axis, + Direction direction, + YGNodeConstRef node) const { return maxOrDefined( resolve( - computeBorder(inlineStartEdge(axis, direction), direction), 0.0f) + computeBorder(inlineStartEdge(axis, direction), direction), + 0.0f, + node) .unwrap(), 0.0f); } - float computeFlexEndBorder(FlexDirection axis, Direction direction) const { + float computeFlexEndBorder( + FlexDirection axis, + Direction direction, + YGNodeConstRef node) const { return maxOrDefined( - resolve(computeBorder(flexEndEdge(axis), direction), 0.0f).unwrap(), + resolve(computeBorder(flexEndEdge(axis), direction), 0.0f, node) + .unwrap(), 0.0f); } - float computeInlineEndBorder(FlexDirection axis, Direction direction) const { + float computeInlineEndBorder( + FlexDirection axis, + Direction direction, + YGNodeConstRef node) const { return maxOrDefined( - resolve(computeBorder(inlineEndEdge(axis, direction), direction), 0.0f) + resolve( + computeBorder(inlineEndEdge(axis, direction), direction), + 0.0f, + node) .unwrap(), 0.0f); } @@ -516,9 +551,10 @@ class YG_EXPORT Style { float computeFlexStartPadding( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return maxOrDefined( - resolve(computePadding(flexStartEdge(axis), direction), widthSize) + resolve(computePadding(flexStartEdge(axis), direction), widthSize, node) .unwrap(), 0.0f); } @@ -526,11 +562,13 @@ class YG_EXPORT Style { float computeInlineStartPadding( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return maxOrDefined( resolve( computePadding(inlineStartEdge(axis, direction), direction), - widthSize) + widthSize, + node) .unwrap(), 0.0f); } @@ -538,9 +576,10 @@ class YG_EXPORT Style { float computeFlexEndPadding( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return maxOrDefined( - resolve(computePadding(flexEndEdge(axis), direction), widthSize) + resolve(computePadding(flexEndEdge(axis), direction), widthSize, node) .unwrap(), 0.0f); } @@ -548,11 +587,13 @@ class YG_EXPORT Style { float computeInlineEndPadding( FlexDirection axis, Direction direction, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { return maxOrDefined( resolve( computePadding(inlineEndEdge(axis, direction), direction), - widthSize) + widthSize, + node) .unwrap(), 0.0f); } @@ -560,70 +601,82 @@ class YG_EXPORT Style { float computeInlineStartPaddingAndBorder( FlexDirection axis, Direction direction, - float widthSize) const { - return computeInlineStartPadding(axis, direction, widthSize) + - computeInlineStartBorder(axis, direction); + float widthSize, + YGNodeConstRef node) const { + return computeInlineStartPadding(axis, direction, widthSize, node) + + computeInlineStartBorder(axis, direction, node); } float computeFlexStartPaddingAndBorder( FlexDirection axis, Direction direction, - float widthSize) const { - return computeFlexStartPadding(axis, direction, widthSize) + - computeFlexStartBorder(axis, direction); + float widthSize, + YGNodeConstRef node) const { + return computeFlexStartPadding(axis, direction, widthSize, node) + + computeFlexStartBorder(axis, direction, node); } float computeInlineEndPaddingAndBorder( FlexDirection axis, Direction direction, - float widthSize) const { - return computeInlineEndPadding(axis, direction, widthSize) + - computeInlineEndBorder(axis, direction); + float widthSize, + YGNodeConstRef node) const { + return computeInlineEndPadding(axis, direction, widthSize, node) + + computeInlineEndBorder(axis, direction, node); } float computeFlexEndPaddingAndBorder( FlexDirection axis, Direction direction, - float widthSize) const { - return computeFlexEndPadding(axis, direction, widthSize) + - computeFlexEndBorder(axis, direction); + float widthSize, + YGNodeConstRef node) const { + return computeFlexEndPadding(axis, direction, widthSize, node) + + computeFlexEndBorder(axis, direction, node); } float computePaddingAndBorderForDimension( Direction direction, Dimension dimension, - float widthSize) const { + float widthSize, + YGNodeConstRef node) const { FlexDirection flexDirectionForDimension = dimension == Dimension::Width ? FlexDirection::Row : FlexDirection::Column; return computeFlexStartPaddingAndBorder( - flexDirectionForDimension, direction, widthSize) + + flexDirectionForDimension, direction, widthSize, node) + computeFlexEndPaddingAndBorder( - flexDirectionForDimension, direction, widthSize); + flexDirectionForDimension, direction, widthSize, node); } - float computeBorderForAxis(FlexDirection axis) const { - return computeInlineStartBorder(axis, Direction::LTR) + - computeInlineEndBorder(axis, Direction::LTR); + float computeBorderForAxis(FlexDirection axis, YGNodeConstRef node) const { + return computeInlineStartBorder(axis, Direction::LTR, node) + + computeInlineEndBorder(axis, Direction::LTR, node); } - float computeMarginForAxis(FlexDirection axis, float widthSize) const { - // The total margin for a given axis does not depend on the direction - // so hardcoding LTR here to avoid piping direction to this function - return computeInlineStartMargin(axis, Direction::LTR, widthSize) + - computeInlineEndMargin(axis, Direction::LTR, widthSize); + float computeMarginForAxis( + FlexDirection axis, + float widthSize, + YGNodeConstRef node) const { + return computeInlineStartMargin(axis, Direction::LTR, widthSize, node) + + computeInlineEndMargin(axis, Direction::LTR, widthSize, node); } - float computeGapForAxis(FlexDirection axis, float ownerSize) const { + float computeGapForAxis( + FlexDirection axis, + float ownerSize, + YGNodeConstRef node) const { auto gap = isRow(axis) ? computeColumnGap() : computeRowGap(); - return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f); + return maxOrDefined(resolve(gap, ownerSize, node).unwrap(), 0.0f); } - float computeGapForDimension(Dimension dimension, float ownerSize) const { + float computeGapForDimension( + Dimension dimension, + float ownerSize, + YGNodeConstRef node) const { auto gap = dimension == Dimension::Width ? computeColumnGap() : computeRowGap(); - return maxOrDefined(resolve(gap, ownerSize).unwrap(), 0.0f); + return maxOrDefined(resolve(gap, ownerSize, node).unwrap(), 0.0f); } bool flexStartMarginIsAuto(FlexDirection axis, Direction direction) const { @@ -861,7 +914,10 @@ class YG_EXPORT Style { * StyleLength/StyleSizeLength object on the stack during hot-path overhead * calculations. */ - FloatOptional resolve(StyleValueHandle handle, float referenceLength) const { + FloatOptional resolve( + StyleValueHandle handle, + float referenceLength, + YGNodeConstRef node) const { if (handle.isPoint()) { return FloatOptional{pool_.getStoredValue(handle)}; } @@ -869,6 +925,17 @@ class YG_EXPORT Style { return FloatOptional{ pool_.getStoredValue(handle) * referenceLength * 0.01f}; } + if (handle.isDynamic()) { + auto callback = pool_.getDynamicCallback(handle); + if (callback) { + return FloatOptional{callback( + node, + pool_.getDynamicCallbackID(handle), + YGValueDynamicContext{referenceLength}) + .value}; + } + } + return FloatOptional{}; } diff --git a/yoga/style/StyleLength.h b/yoga/style/StyleLength.h index 8099ce7df4..500f3265a9 100644 --- a/yoga/style/StyleLength.h +++ b/yoga/style/StyleLength.h @@ -49,6 +49,10 @@ class StyleLength { return StyleLength{{}, Unit::Undefined}; } + static StyleLength dynamic(YGValueDynamic callback, YGValueDynamicID id) { + return StyleLength{callback, id}; + } + constexpr bool isAuto() const { return unit_ == Unit::Auto; } @@ -65,15 +69,31 @@ class StyleLength { return unit_ == Unit::Percent; } + constexpr bool isDynamic() const { + return unit_ == Unit::Dynamic; + } + constexpr bool isDefined() const { return !isUndefined(); } constexpr FloatOptional value() const { - return value_; + if (isDynamic()) { + return FloatOptional{}; + } + return payload_.value; + } + + YGValueDynamic callback() const { + return isDynamic() ? payload_.dynamic.callback : nullptr; + } + + constexpr YGValueDynamicID callbackId() const { + return isDynamic() ? payload_.dynamic.id : 0; } - constexpr FloatOptional resolve(float referenceLength) { + constexpr FloatOptional resolve(float referenceLength, YGNodeConstRef node) + const { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wswitch-enum" @@ -83,34 +103,68 @@ class StyleLength { #pragma clang diagnostic pop #endif case Unit::Point: - return value_; + return payload_.value; case Unit::Percent: - return FloatOptional{value_.unwrap() * referenceLength * 0.01f}; + return FloatOptional{payload_.value.unwrap() * referenceLength * 0.01f}; + case Unit::Dynamic: + if (payload_.dynamic.callback != nullptr && node != nullptr) { + auto value = payload_.dynamic.callback( + node, + payload_.dynamic.id, + YGValueDynamicContext{referenceLength}); + return FloatOptional{value.value}; + } + return FloatOptional{}; default: return FloatOptional{}; } } explicit constexpr operator YGValue() const { - return YGValue{value_.unwrap(), unscopedEnum(unit_)}; + return YGValue{value().unwrap(), unscopedEnum(unit_)}; } constexpr bool operator==(const StyleLength& rhs) const { - return value_ == rhs.value_ && unit_ == rhs.unit_; + if (unit_ != rhs.unit_) { + return false; + } + if (isDynamic()) { + return payload_.dynamic.callback == rhs.payload_.dynamic.callback && + payload_.dynamic.id == rhs.payload_.dynamic.id; + } + return payload_.value == rhs.payload_.value; } constexpr bool inexactEquals(const StyleLength& other) const { - return unit_ == other.unit_ && - facebook::yoga::inexactEquals(value_, other.value_); + if (unit_ != other.unit_) { + return false; + } + if (isDynamic()) { + return payload_.dynamic.callback == other.payload_.dynamic.callback && + payload_.dynamic.id == other.payload_.dynamic.id; + } + return facebook::yoga::inexactEquals(payload_.value, other.payload_.value); } private: + union Payload { + constexpr Payload() : value{} {} + constexpr explicit Payload(FloatOptional val) : value(val) {} + constexpr Payload(YGValueDynamic callback, YGValueDynamicID id) + : dynamic{callback, id} {} + + FloatOptional value; + YGValueDynamicData dynamic; + }; + // We intentionally do not allow direct construction using value and unit, to // avoid invalid, or redundant combinations. constexpr StyleLength(FloatOptional value, Unit unit) - : value_(value), unit_(unit) {} + : payload_(value), unit_(unit) {} + constexpr StyleLength(YGValueDynamic callback, YGValueDynamicID id) + : payload_(callback, id), unit_(Unit::Dynamic) {} - FloatOptional value_{}; + Payload payload_{}; Unit unit_{Unit::Undefined}; }; diff --git a/yoga/style/StyleSizeLength.h b/yoga/style/StyleSizeLength.h index 76e079b2da..8f112e1211 100644 --- a/yoga/style/StyleSizeLength.h +++ b/yoga/style/StyleSizeLength.h @@ -68,6 +68,10 @@ class StyleSizeLength { return StyleSizeLength{{}, Unit::Undefined}; } + static StyleSizeLength dynamic(YGValueDynamic callback, YGValueDynamicID id) { + return StyleSizeLength{callback, id}; + } + constexpr bool isAuto() const { return unit_ == Unit::Auto; } @@ -100,11 +104,27 @@ class StyleSizeLength { return unit_ == Unit::Percent; } + constexpr bool isDynamic() const { + return unit_ == Unit::Dynamic; + } + constexpr FloatOptional value() const { - return value_; + if (isDynamic()) { + return FloatOptional{}; + } + return payload_.value; + } + + YGValueDynamic callback() const { + return isDynamic() ? payload_.dynamic.callback : nullptr; + } + + constexpr YGValueDynamicID callbackId() const { + return isDynamic() ? payload_.dynamic.id : 0; } - constexpr FloatOptional resolve(float referenceLength) const { + constexpr FloatOptional resolve(float referenceLength, YGNodeConstRef node) + const { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wswitch-enum" @@ -114,34 +134,68 @@ class StyleSizeLength { #pragma clang diagnostic pop #endif case Unit::Point: - return value_; + return payload_.value; case Unit::Percent: - return FloatOptional{value_.unwrap() * referenceLength * 0.01f}; + return FloatOptional{payload_.value.unwrap() * referenceLength * 0.01f}; + case Unit::Dynamic: + if (payload_.dynamic.callback != nullptr && node != nullptr) { + auto value = payload_.dynamic.callback( + node, + payload_.dynamic.id, + YGValueDynamicContext{referenceLength}); + return FloatOptional{value.value}; + } + return FloatOptional{}; default: return FloatOptional{}; } } explicit constexpr operator YGValue() const { - return YGValue{value_.unwrap(), unscopedEnum(unit_)}; + return YGValue{value().unwrap(), unscopedEnum(unit_)}; } constexpr bool operator==(const StyleSizeLength& rhs) const { - return value_ == rhs.value_ && unit_ == rhs.unit_; + if (unit_ != rhs.unit_) { + return false; + } + if (isDynamic()) { + return payload_.dynamic.callback == rhs.payload_.dynamic.callback && + payload_.dynamic.id == rhs.payload_.dynamic.id; + } + return payload_.value == rhs.payload_.value; } constexpr bool inexactEquals(const StyleSizeLength& other) const { - return unit_ == other.unit_ && - facebook::yoga::inexactEquals(value_, other.value_); + if (unit_ != other.unit_) { + return false; + } + if (isDynamic()) { + return payload_.dynamic.callback == other.payload_.dynamic.callback && + payload_.dynamic.id == other.payload_.dynamic.id; + } + return facebook::yoga::inexactEquals(payload_.value, other.payload_.value); } private: + union Payload { + constexpr Payload() : value{} {} + constexpr explicit Payload(FloatOptional val) : value(val) {} + constexpr Payload(YGValueDynamic callback, YGValueDynamicID id) + : dynamic{callback, id} {} + + FloatOptional value; + YGValueDynamicData dynamic; + }; + // We intentionally do not allow direct construction using value and unit, to // avoid invalid, or redundant combinations. constexpr StyleSizeLength(FloatOptional value, Unit unit) - : value_(value), unit_(unit) {} + : payload_(value), unit_(unit) {} + constexpr StyleSizeLength(YGValueDynamic callback, YGValueDynamicID id) + : payload_(callback, id), unit_(Unit::Dynamic) {} - FloatOptional value_{}; + Payload payload_{}; Unit unit_{Unit::Undefined}; }; diff --git a/yoga/style/StyleValueHandle.h b/yoga/style/StyleValueHandle.h index 6a2b589023..0009a20f3c 100644 --- a/yoga/style/StyleValueHandle.h +++ b/yoga/style/StyleValueHandle.h @@ -57,6 +57,10 @@ class StyleValueHandle { return type() == Type::Point; } + constexpr bool isDynamic() const { + return type() == Type::Dynamic; + } + private: friend class StyleValuePool; @@ -70,7 +74,8 @@ class StyleValueHandle { Percent, Number, Auto, - Keyword + Keyword, + Dynamic }; // Intentionally leaving out auto as a fast path diff --git a/yoga/style/StyleValuePool.h b/yoga/style/StyleValuePool.h index fb31193b97..135cb90aff 100644 --- a/yoga/style/StyleValuePool.h +++ b/yoga/style/StyleValuePool.h @@ -32,6 +32,8 @@ class StyleValuePool { handle.setType(StyleValueHandle::Type::Undefined); } else if (length.isAuto()) { handle.setType(StyleValueHandle::Type::Auto); + } else if (length.isDynamic()) { + storeDynamic(handle, length.callback(), length.callbackId()); } else { auto type = length.isPoints() ? StyleValueHandle::Type::Point : StyleValueHandle::Type::Percent; @@ -50,6 +52,8 @@ class StyleValuePool { storeKeyword(handle, StyleValueHandle::Keyword::Stretch); } else if (sizeValue.isFitContent()) { storeKeyword(handle, StyleValueHandle::Keyword::FitContent); + } else if (sizeValue.isDynamic()) { + storeDynamic(handle, sizeValue.callback(), sizeValue.callbackId()); } else { auto type = sizeValue.isPoints() ? StyleValueHandle::Type::Point : StyleValueHandle::Type::Percent; @@ -70,6 +74,9 @@ class StyleValuePool { return StyleLength::undefined(); } else if (handle.isAuto()) { return StyleLength::ofAuto(); + } else if (handle.isDynamic()) { + return StyleLength::dynamic( + getDynamicCallback(handle), getDynamicCallbackID(handle)); } else { assert( handle.type() == StyleValueHandle::Type::Point || @@ -95,6 +102,9 @@ class StyleValuePool { return StyleSizeLength::ofFitContent(); } else if (handle.isKeyword(StyleValueHandle::Keyword::Stretch)) { return StyleSizeLength::ofStretch(); + } else if (handle.isDynamic()) { + return StyleSizeLength::dynamic( + getDynamicCallback(handle), getDynamicCallbackID(handle)); } else { assert( handle.type() == StyleValueHandle::Type::Point || @@ -131,6 +141,19 @@ class StyleValuePool { : unpackInlineInteger(handle.value()); } + YGValueDynamic getDynamicCallback(StyleValueHandle handle) const { + assert(handle.isDynamic()); + assert(handle.isValueIndexed()); + return reinterpret_cast( + static_cast(buffer_.get64(handle.value()))); + } + + YGValueDynamicID getDynamicCallbackID(StyleValueHandle handle) const { + assert(handle.isDynamic()); + assert(handle.isValueIndexed()); + return buffer_.get32(handle.value() + 2); + } + private: void storeValue( StyleValueHandle& handle, @@ -165,6 +188,31 @@ class StyleValuePool { } } + void storeDynamic( + StyleValueHandle& handle, + YGValueDynamic callback, + YGValueDynamicID id) { + handle.setType(StyleValueHandle::Type::Dynamic); + auto packed = static_cast(reinterpret_cast(callback)); + + if (handle.isValueIndexed()) { + auto oldIndex = handle.value(); + auto newIndex = buffer_.replace(oldIndex, packed); + if (newIndex == oldIndex) { + [[maybe_unused]] auto replacedIndex = buffer_.replace( + static_cast(newIndex + 2), static_cast(id)); + } else { + buffer_.push(static_cast(id)); + } + handle.setValue(newIndex); + } else { + auto newIndex = buffer_.push(packed); + buffer_.push(static_cast(id)); + handle.setValue(newIndex); + handle.setValueIsIndexed(); + } + } + static constexpr bool isIntegerPackable(float f) { constexpr uint16_t kMaxInlineAbsValue = (1 << 11) - 1; From 58a346480fa92767764cbd9afa807221e6309b2a Mon Sep 17 00:00:00 2001 From: Kamil Paradowski Date: Fri, 20 Mar 2026 12:25:32 -0300 Subject: [PATCH 2/5] refactor: move YGValueDynamicData into style length classes --- yoga/YGValue.h | 8 -------- yoga/style/StyleLength.h | 5 +++++ yoga/style/StyleSizeLength.h | 5 +++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/yoga/YGValue.h b/yoga/YGValue.h index 5666880626..404bc8d029 100644 --- a/yoga/YGValue.h +++ b/yoga/YGValue.h @@ -77,14 +77,6 @@ typedef YGValue (*YGValueDynamic)( YGValueDynamicID id, YGValueDynamicContext context); -/** - * Callback + identifier pair for internal storage. - */ -struct YGValueDynamicData { - YGValueDynamic callback; - YGValueDynamicID id; -}; - YG_EXTERN_C_END // Equality operators for comparison of YGValue in C++ diff --git a/yoga/style/StyleLength.h b/yoga/style/StyleLength.h index 500f3265a9..5a833da58e 100644 --- a/yoga/style/StyleLength.h +++ b/yoga/style/StyleLength.h @@ -147,6 +147,11 @@ class StyleLength { } private: + struct YGValueDynamicData { + YGValueDynamic callback; + YGValueDynamicID id; + }; + union Payload { constexpr Payload() : value{} {} constexpr explicit Payload(FloatOptional val) : value(val) {} diff --git a/yoga/style/StyleSizeLength.h b/yoga/style/StyleSizeLength.h index 8f112e1211..9a67cd0cb8 100644 --- a/yoga/style/StyleSizeLength.h +++ b/yoga/style/StyleSizeLength.h @@ -178,6 +178,11 @@ class StyleSizeLength { } private: + struct YGValueDynamicData { + YGValueDynamic callback; + YGValueDynamicID id; + }; + union Payload { constexpr Payload() : value{} {} constexpr explicit Payload(FloatOptional val) : value(val) {} From 916eafc21dd9bd4dc14dcfeac18d4c2f5b2c68ac Mon Sep 17 00:00:00 2001 From: Kamil Paradowski Date: Fri, 20 Mar 2026 12:28:25 -0300 Subject: [PATCH 3/5] fix: add test coverage for dynamic style values --- tests/StyleDynamicValueTest.cpp | 86 +++++++++ tests/StyleValuePoolTest.cpp | 25 +++ tests/YGDynamicValueTest.cpp | 297 ++++++++++++++++++++++++++++++++ 3 files changed, 408 insertions(+) create mode 100644 tests/StyleDynamicValueTest.cpp create mode 100644 tests/YGDynamicValueTest.cpp diff --git a/tests/StyleDynamicValueTest.cpp b/tests/StyleDynamicValueTest.cpp new file mode 100644 index 0000000000..dc903c7c3f --- /dev/null +++ b/tests/StyleDynamicValueTest.cpp @@ -0,0 +1,86 @@ +/* + * 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 + +namespace facebook::yoga { + +static YGValue _resolveToHalfReference( + YGNodeConstRef /*node*/, + YGValueDynamicID /*id*/, + YGValueDynamicContext context) { + return YGValue{context.referenceLength * 0.5f, YGUnitPoint}; +} + +static YGValue _resolveToZero( + YGNodeConstRef /*node*/, + YGValueDynamicID /*id*/, + YGValueDynamicContext /*context*/) { + return YGValue{0, YGUnitPoint}; +} + +TEST(StyleLength, dynamic_resolve_returns_undefined_without_node) { + const auto resolved = + StyleLength::dynamic(_resolveToHalfReference, 1).resolve(100.0f, nullptr); + + ASSERT_TRUE(resolved.isUndefined()); +} + +TEST(StyleLength, dynamic_resolve_returns_undefined_without_callback) { + const Node node{}; + const auto resolved = StyleLength::dynamic(nullptr, 1).resolve(100.0f, &node); + + ASSERT_TRUE(resolved.isUndefined()); +} + +TEST(StyleLength, dynamic_resolve_calls_callback) { + const Node node{}; + const auto resolved = + StyleLength::dynamic(_resolveToHalfReference, 1).resolve(80.0f, &node); + + ASSERT_TRUE(resolved.isDefined()); + ASSERT_FLOAT_EQ(resolved.unwrap(), 40.0f); +} + +TEST(StyleSizeLength, dynamic_resolve_calls_callback) { + const Node node{}; + const auto resolved = StyleSizeLength::dynamic(_resolveToHalfReference, 1) + .resolve(80.0f, &node); + + ASSERT_TRUE(resolved.isDefined()); + ASSERT_FLOAT_EQ(resolved.unwrap(), 40.0f); +} + +TEST(StyleLength, dynamic_equality) { + const auto a = StyleLength::dynamic(_resolveToHalfReference, 1); + const auto b = StyleLength::dynamic(_resolveToHalfReference, 1); + const auto differentId = StyleLength::dynamic(_resolveToHalfReference, 2); + const auto differentCallback = StyleLength::dynamic(_resolveToZero, 1); + const auto points = StyleLength::points(50); + + ASSERT_TRUE(a == b); + ASSERT_FALSE(a == differentId); + ASSERT_FALSE(a == differentCallback); + ASSERT_FALSE(a == points); + ASSERT_TRUE(a.inexactEquals(b)); + ASSERT_FALSE(a.inexactEquals(differentId)); + ASSERT_FALSE(a.inexactEquals(differentCallback)); +} + +TEST(StyleLength, dynamic_ygvalue_conversion) { + const auto length = StyleLength::dynamic(_resolveToHalfReference, 1); + const YGValue ygValue = static_cast(length); + + ASSERT_EQ(ygValue.unit, YGUnitDynamic); + ASSERT_TRUE(YGFloatIsUndefined(ygValue.value)); +} + +} // namespace facebook::yoga \ No newline at end of file diff --git a/tests/StyleValuePoolTest.cpp b/tests/StyleValuePoolTest.cpp index ff38acff36..31509fcc9f 100644 --- a/tests/StyleValuePoolTest.cpp +++ b/tests/StyleValuePoolTest.cpp @@ -10,6 +10,11 @@ namespace facebook::yoga { +static YGValue +dynamicValueCallback(YGNodeConstRef, YGValueDynamicID, YGValueDynamicContext) { + return YGValue{0.0f, YGUnitPoint}; +} + TEST(StyleValuePool, undefined_at_init) { StyleValuePool pool; StyleValueHandle handle; @@ -143,4 +148,24 @@ TEST(StyleValuePool, store_keywords) { EXPECT_EQ(pool.getSize(handleStretch), StyleSizeLength::ofStretch()); } +TEST(StyleValuePool, stores_and_recovers_dynamic) { + StyleValuePool pool; + StyleValueHandle lengthHandle; + StyleValueHandle sizeHandle; + + pool.store(lengthHandle, StyleLength::dynamic(dynamicValueCallback, 11)); + pool.store(sizeHandle, StyleSizeLength::dynamic(dynamicValueCallback, 12)); + + const auto restoredLength = pool.getLength(lengthHandle); + const auto restoredSize = pool.getSize(sizeHandle); + + EXPECT_TRUE(restoredLength.isDynamic()); + EXPECT_EQ(restoredLength.callback(), dynamicValueCallback); + EXPECT_EQ(restoredLength.callbackId(), 11); + + EXPECT_TRUE(restoredSize.isDynamic()); + EXPECT_EQ(restoredSize.callback(), dynamicValueCallback); + EXPECT_EQ(restoredSize.callbackId(), 12); +} + } // namespace facebook::yoga diff --git a/tests/YGDynamicValueTest.cpp b/tests/YGDynamicValueTest.cpp new file mode 100644 index 0000000000..cfc0096565 --- /dev/null +++ b/tests/YGDynamicValueTest.cpp @@ -0,0 +1,297 @@ +/* + * 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 + +static YGValue _resolveToFixedValue( + YGNodeConstRef /*node*/, + YGValueDynamicID /*id*/, + YGValueDynamicContext /*context*/) { + return YGValue{50, YGUnitPoint}; +} + +static YGValue _resolveToHalfReference( + YGNodeConstRef /*node*/, + YGValueDynamicID /*id*/, + YGValueDynamicContext context) { + return YGValue{context.referenceLength * 0.5f, YGUnitPoint}; +} + +static YGValue _resolveByID( + YGNodeConstRef /*node*/, + YGValueDynamicID id, + YGValueDynamicContext /*context*/) { + switch (id) { + case 1: + return YGValue{10, YGUnitPoint}; + case 2: + return YGValue{20, YGUnitPoint}; + default: + return YGValue{0, YGUnitPoint}; + } +} + +TEST(YogaTest, dynamic_width) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidthDynamic(root, _resolveToFixedValue, 0); + YGNodeStyleSetHeight(root, 100); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_height) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeightDynamic(root, _resolveToFixedValue, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_flex_basis) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexBasisDynamic(root_child0, _resolveToFixedValue, 0); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_margin) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetMarginDynamic(root_child0, YGEdgeLeft, _resolveToFixedValue, 0); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_padding) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + YGNodeStyleSetPaddingDynamic(root, YGEdgeAll, _resolveToFixedValue, 0); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_border) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + YGNodeStyleSetBorderDynamic(root, YGEdgeAll, _resolveToFixedValue, 0); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_position) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPositionDynamic( + root_child0, YGEdgeLeft, _resolveToFixedValue, 0); + YGNodeStyleSetPositionDynamic( + root_child0, YGEdgeTop, _resolveToFixedValue, 0); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_gap) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetGapDynamic(root, YGGutterColumn, _resolveToFixedValue, 0); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_min_width) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetMinWidthDynamic(root_child0, _resolveToFixedValue, 0); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_max_height) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMaxHeightDynamic(root_child0, _resolveToFixedValue, 0); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_value_receives_reference_length) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidthDynamic(root_child0, _resolveToHalfReference, 0); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + // Child width resolves to half of parent width (200 * 0.5 = 100) + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_value_id_disambiguates_properties) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + // ID 1 resolves to 10, ID 2 resolves to 20 + YGNodeStyleSetPositionDynamic(root_child0, YGEdgeLeft, _resolveByID, 1); + YGNodeStyleSetPositionDynamic(root_child0, YGEdgeTop, _resolveByID, 2); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_value_overwritten_by_point_value) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidthDynamic(root, _resolveToFixedValue, 0); + YGNodeStyleSetHeight(root, 100); + + // Overwrite dynamic with a concrete value + YGNodeStyleSetWidth(root, 80); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, point_value_overwritten_by_dynamic_value) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 80); + YGNodeStyleSetHeight(root, 100); + + // Overwrite concrete value with dynamic + YGNodeStyleSetWidthDynamic(root, _resolveToFixedValue, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dynamic_value_style_get_returns_dynamic_unit) { + YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidthDynamic(root, _resolveToFixedValue, 0); + + YGValue width = YGNodeStyleGetWidth(root); + ASSERT_EQ(width.unit, YGUnitDynamic); + + YGNodeFreeRecursive(root); +} From 5e4773f0b419ddc6453c37a9181ac5c5c3c6d0c0 Mon Sep 17 00:00:00 2001 From: Kamil Paradowski Date: Fri, 20 Mar 2026 13:02:50 -0300 Subject: [PATCH 4/5] fix: store dynamic value ids as uint32_t --- yoga/YGValue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yoga/YGValue.h b/yoga/YGValue.h index 404bc8d029..27f7524d2d 100644 --- a/yoga/YGValue.h +++ b/yoga/YGValue.h @@ -58,7 +58,7 @@ YG_EXPORT bool YGFloatIsUndefined(float value); /** * Host-defined identifier for a dynamic style value. */ -typedef uint8_t YGValueDynamicID; +typedef uint32_t YGValueDynamicID; /** * Layout context passed to YGValueDynamic for resolving dynamic values. From 5479eacaacfa39062d824d88bbe42556f2c29623 Mon Sep 17 00:00:00 2001 From: Kamil Paradowski Date: Mon, 23 Mar 2026 17:04:54 -0300 Subject: [PATCH 5/5] fix: restore accidentally dropped comment --- yoga/style/Style.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yoga/style/Style.h b/yoga/style/Style.h index 556b28d71e..5da169a6fe 100644 --- a/yoga/style/Style.h +++ b/yoga/style/Style.h @@ -658,6 +658,8 @@ class YG_EXPORT Style { FlexDirection axis, float widthSize, YGNodeConstRef node) const { + // The total margin for a given axis does not depend on the direction + // so hardcoding LTR here to avoid piping direction to this function return computeInlineStartMargin(axis, Direction::LTR, widthSize, node) + computeInlineEndMargin(axis, Direction::LTR, widthSize, node); }