diff --git a/tests/YGNodeCallbackTest.cpp b/tests/YGNodeCallbackTest.cpp index 234fc9c1..6704d8d2 100644 --- a/tests/YGNodeCallbackTest.cpp +++ b/tests/YGNodeCallbackTest.cpp @@ -36,7 +36,36 @@ TEST(YGNode, measure_with_measure_fn) { }); ASSERT_EQ( - n.measure(23, YGMeasureModeExactly, 24, YGMeasureModeAtMost), + n.measure(23, YGMeasureModeExactly, 24, YGMeasureModeAtMost, nullptr), + (YGSize{23, 12})); +} + +TEST(YGNode, measure_with_context_measure_fn) { + auto n = YGNode{}; + n.setMeasureFunc( + [](YGNode*, float, YGMeasureMode, float, YGMeasureMode, void* ctx) { + return *(YGSize*) ctx; + }); + + auto result = YGSize{123.4, -56.7}; + ASSERT_EQ( + n.measure(0, YGMeasureModeUndefined, 0, YGMeasureModeUndefined, &result), + result); +} + +TEST(YGNode, switching_measure_fn_types) { + auto n = YGNode{}; + n.setMeasureFunc( + [](YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*) { + return YGSize{}; + }); + n.setMeasureFunc( + [](YGNode*, float w, YGMeasureMode wm, float h, YGMeasureMode hm) { + return YGSize{w * wm, h / hm}; + }); + + ASSERT_EQ( + n.measure(23, YGMeasureModeExactly, 24, YGMeasureModeAtMost, nullptr), (YGSize{23, 12})); } @@ -50,6 +79,17 @@ TEST(YGNode, hasMeasureFunc_after_unset) { ASSERT_FALSE(n.hasMeasureFunc()); } +TEST(YGNode, hasMeasureFunc_after_unset_context) { + auto n = YGNode{}; + n.setMeasureFunc( + [](YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*) { + return YGSize{}; + }); + + n.setMeasureFunc(nullptr); + ASSERT_FALSE(n.hasMeasureFunc()); +} + TEST(YGNode, hasBaselineFunc_initial) { auto n = YGNode{}; ASSERT_FALSE(n.hasBaselineFunc()); @@ -65,7 +105,17 @@ TEST(YGNode, baseline_with_baseline_fn) { auto n = YGNode{}; n.setBaseLineFunc([](YGNode*, float w, float h) { return w + h; }); - ASSERT_EQ(n.baseline(1.25f, 2.5f), 3.75f); + ASSERT_EQ(n.baseline(1.25f, 2.5f, nullptr), 3.75f); +} + +TEST(YGNode, baseline_with_context_baseline_fn) { + auto n = YGNode{}; + n.setBaseLineFunc([](YGNode*, float w, float h, void* ctx) { + return w + h + *(float*) ctx; + }); + + auto ctx = -10.0f; + ASSERT_EQ(n.baseline(1.25f, 2.5f, &ctx), -6.25f); } TEST(YGNode, hasBaselineFunc_after_unset) { @@ -76,6 +126,21 @@ TEST(YGNode, hasBaselineFunc_after_unset) { ASSERT_FALSE(n.hasBaselineFunc()); } +TEST(YGNode, hasBaselineFunc_after_unset_context) { + auto n = YGNode{}; + n.setBaseLineFunc([](YGNode*, float, float, void*) { return 0.0f; }); + + n.setMeasureFunc(nullptr); + ASSERT_FALSE(n.hasMeasureFunc()); +} + +TEST(YGNode, switching_baseline_fn_types) { + auto n = YGNode{}; + n.setBaseLineFunc([](YGNode*, float, float, void*) { return 0.0f; }); + n.setBaseLineFunc([](YGNode*, float, float) { return 1.0f; }); + ASSERT_EQ(n.baseline(1, 2, nullptr), 1.0f); +} + void PrintTo(const YGSize& size, std::ostream* os) { *os << "YGSize{" << size.width << ", " << size.height << "}"; } diff --git a/yoga/YGNode.cpp b/yoga/YGNode.cpp index 9b06cb9c..303df5bc 100644 --- a/yoga/YGNode.cpp +++ b/yoga/YGNode.cpp @@ -101,11 +101,29 @@ YGFloatOptional YGNode::getMarginForAxis( return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize); } +YGSize YGNode::measure( + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode, + void* layoutContext) { + + return measureUsesContext_ + ? measure_.withContext( + this, width, widthMode, height, heightMode, layoutContext) + : measure_.noContext(this, width, widthMode, height, heightMode); +} + +float YGNode::baseline(float width, float height, void* layoutContext) { + return baselineUsesContext_ + ? baseline_.withContext(this, width, height, layoutContext) + : baseline_.noContext(this, width, height); +} + // Setters -void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { - if (measureFunc == nullptr) { - measure_ = nullptr; +void YGNode::setMeasureFunc(decltype(YGNode::measure_) measureFunc) { + if (measureFunc.noContext == nullptr) { // TODO: t18095186 Move nodeType to opt-in function and mark appropriate // places in Litho nodeType_ = YGNodeTypeDefault; @@ -115,7 +133,6 @@ void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { children_.size() == 0, "Cannot set measure function: Nodes with measure functions cannot have " "children."); - measure_ = measureFunc; // TODO: t18095186 Move nodeType to opt-in function and mark appropriate // places in Litho setNodeType(YGNodeTypeText); @@ -124,6 +141,20 @@ void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { measure_ = measureFunc; } +void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { + measureUsesContext_ = false; + decltype(YGNode::measure_) m; + m.noContext = measureFunc; + setMeasureFunc(m); +} + +void YGNode::setMeasureFunc(MeasureWithContextFn measureFunc) { + measureUsesContext_ = true; + decltype(YGNode::measure_) m; + m.withContext = measureFunc; + setMeasureFunc(m); +} + void YGNode::replaceChild(YGNodeRef child, uint32_t index) { children_[index] = child; } @@ -270,6 +301,8 @@ YGNode& YGNode::operator=(const YGNode& node) { print_ = node.getPrintFunc(); hasNewLayout_ = node.getHasNewLayout(); nodeType_ = node.getNodeType(); + measureUsesContext_ = node.measureUsesContext_; + baselineUsesContext_ = node.baselineUsesContext_; measure_ = node.measure_; baseline_ = node.baseline_; dirtied_ = node.getDirtied(); diff --git a/yoga/YGNode.h b/yoga/YGNode.h index 776af0f9..ddb5fa07 100644 --- a/yoga/YGNode.h +++ b/yoga/YGNode.h @@ -12,6 +12,10 @@ #include "Yoga-internal.h" struct YGNode { + using MeasureWithContextFn = + YGSize (*)(YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*); + using BaselineWithContextFn = float (*)(YGNode*, float, float, void*); + private: void* context_ = nullptr; YGPrintFunc print_ = nullptr; @@ -19,8 +23,16 @@ private: bool isReferenceBaseline_ : 1; bool isDirty_ : 1; YGNodeType nodeType_ : 1; - YGMeasureFunc measure_ = nullptr; - YGBaselineFunc baseline_ = nullptr; + bool measureUsesContext_ : 1; + bool baselineUsesContext_ : 1; + union { + YGMeasureFunc noContext; + MeasureWithContextFn withContext; + } measure_ = {nullptr}; + union { + YGBaselineFunc noContext; + BaselineWithContextFn withContext; + } baseline_ = {nullptr}; YGDirtiedFunc dirtied_ = nullptr; YGStyle style_ = {}; YGLayout layout_ = {}; @@ -35,12 +47,17 @@ private: const YGFlexDirection axis, const float axisSize) const; + void setMeasureFunc(decltype(measure_)); + void setBaseLineFunc(decltype(baseline_)); + public: YGNode() - : hasNewLayout_(true), - isReferenceBaseline_(false), - isDirty_(false), - nodeType_(YGNodeTypeDefault) {} + : hasNewLayout_{true}, + isReferenceBaseline_{false}, + isDirty_{false}, + nodeType_{YGNodeTypeDefault}, + measureUsesContext_{false}, + baselineUsesContext_{false} {} ~YGNode() = default; // cleanup of owner/children relationships in YGNodeFree explicit YGNode(const YGConfigRef newConfig) : config_(newConfig){}; YGNode(const YGNode& node) = default; @@ -64,24 +81,16 @@ public: } bool hasMeasureFunc() const noexcept { - return measure_ != nullptr; + return measure_.noContext != nullptr; } - YGSize measure( - float width, - YGMeasureMode widthMode, - float height, - YGMeasureMode heightMode) { - return measure_(this, width, widthMode, height, heightMode); - } + YGSize measure(float, YGMeasureMode, float, YGMeasureMode, void*); bool hasBaselineFunc() const noexcept { - return baseline_ != nullptr; + return baseline_.noContext != nullptr; } - float baseline(float width, float height) { - return baseline_(this, width, height); - } + float baseline(float width, float height, void* layoutContext); YGDirtiedFunc getDirtied() const { return dirtied_; @@ -209,9 +218,21 @@ public: } void setMeasureFunc(YGMeasureFunc measureFunc); + void setMeasureFunc(MeasureWithContextFn); + void setMeasureFunc(std::nullptr_t) { + return setMeasureFunc(YGMeasureFunc{nullptr}); + } void setBaseLineFunc(YGBaselineFunc baseLineFunc) { - baseline_ = baseLineFunc; + baselineUsesContext_ = false; + baseline_.noContext = baseLineFunc; + } + void setBaseLineFunc(BaselineWithContextFn baseLineFunc) { + baselineUsesContext_ = true; + baseline_.withContext = baseLineFunc; + } + void setBaseLineFunc(std::nullptr_t) { + return setBaseLineFunc(YGBaselineFunc{nullptr}); } void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { diff --git a/yoga/Yoga.cpp b/yoga/Yoga.cpp index 2559b80b..9f77dc2b 100644 --- a/yoga/Yoga.cpp +++ b/yoga/Yoga.cpp @@ -1080,7 +1080,8 @@ static float YGBaseline(const YGNodeRef node) { node, &YGNode::baseline, node->getLayout().measuredDimensions[YGDimensionWidth], - node->getLayout().measuredDimensions[YGDimensionHeight]); + node->getLayout().measuredDimensions[YGDimensionHeight], + (void*) nullptr); YGAssertWithNode( node, !YGFloatIsUndefined(baseline), @@ -1697,7 +1698,8 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions( innerWidth, widthMeasureMode, innerHeight, - heightMeasureMode); + heightMeasureMode, + (void*) nullptr); node->setLayoutMeasuredDimension( YGNodeBoundAxis(