From 7cfb7b8d0f205b29184888e4837509e1da50ec60 Mon Sep 17 00:00:00 2001 From: Lukas Woehrl Date: Fri, 6 Jan 2017 06:51:56 -0800 Subject: [PATCH] Baseline support Summary: Added baseline support (see #132) You have the ability for a custom baseline function (```float(*YGBaselineFunc)(YGNodeRef node);```) to return whatever baseline you want. Closes https://github.com/facebook/yoga/pull/317 Reviewed By: splhack Differential Revision: D4385061 Pulled By: emilsjolander fbshipit-source-id: cb8a59a09237c840fa3e21753ab68239997dab0c --- .../java/com/facebook/yoga/YogaAlign.java | 4 +- ReactCommon/yoga/yoga/YGEnums.h | 3 +- ReactCommon/yoga/yoga/Yoga.c | 122 ++++++++++++++++-- ReactCommon/yoga/yoga/Yoga.h | 2 + 4 files changed, 116 insertions(+), 15 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaAlign.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaAlign.java index 74bcde7326..7466dcda90 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaAlign.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaAlign.java @@ -17,7 +17,8 @@ public enum YogaAlign { FLEX_START(1), CENTER(2), FLEX_END(3), - STRETCH(4); + STRETCH(4), + BASELINE(5); private int mIntValue; @@ -36,6 +37,7 @@ public enum YogaAlign { case 2: return CENTER; case 3: return FLEX_END; case 4: return STRETCH; + case 5: return BASELINE; default: throw new IllegalArgumentException("Unkown enum value: " + value); } } diff --git a/ReactCommon/yoga/yoga/YGEnums.h b/ReactCommon/yoga/yoga/YGEnums.h index be60e02cdb..24606bcd0b 100644 --- a/ReactCommon/yoga/yoga/YGEnums.h +++ b/ReactCommon/yoga/yoga/YGEnums.h @@ -104,13 +104,14 @@ typedef enum YGExperimentalFeature { YGExperimentalFeatureWebFlexBasis, } YGExperimentalFeature; -#define YGAlignCount 5 +#define YGAlignCount 6 typedef enum YGAlign { YGAlignAuto, YGAlignFlexStart, YGAlignCenter, YGAlignFlexEnd, YGAlignStretch, + YGAlignBaseline, } YGAlign; #define YGUnitCount 3 diff --git a/ReactCommon/yoga/yoga/Yoga.c b/ReactCommon/yoga/yoga/Yoga.c index bc73ecbc38..d5e4917b3e 100644 --- a/ReactCommon/yoga/yoga/Yoga.c +++ b/ReactCommon/yoga/yoga/Yoga.c @@ -102,6 +102,7 @@ typedef struct YGNode { struct YGNode *nextChild; YGMeasureFunc measure; + YGBaselineFunc baseline; YGPrintFunc print; void *context; @@ -337,6 +338,14 @@ YGMeasureFunc YGNodeGetMeasureFunc(const YGNodeRef node) { return node->measure; } +void YGNodeSetBaselineFunc(const YGNodeRef node, YGBaselineFunc baselineFunc) { + node->baseline = baselineFunc; +} + +YGBaselineFunc YGNodeGetBaselineFunc(const YGNodeRef node) { + return node->baseline; +} + void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) { YG_ASSERT(child->parent == NULL, "Child already has a parent, it must be removed first."); YG_ASSERT(node->measure == NULL, @@ -893,8 +902,8 @@ static float YGNodeLeadingPadding(const YGNodeRef node, } return fmaxf(YGValueResolve(YGComputedEdgeValue(node->style.padding, leading[axis], &YGValueZero), - widthSize), - 0.0f); + widthSize), + 0.0f); } static float YGNodeTrailingPadding(const YGNodeRef node, @@ -906,8 +915,8 @@ static float YGNodeTrailingPadding(const YGNodeRef node, } return fmaxf(YGValueResolve(YGComputedEdgeValue(node->style.padding, trailing[axis], &YGValueZero), - widthSize), - 0.0f); + widthSize), + 0.0f); } static float YGNodeLeadingBorder(const YGNodeRef node, const YGFlexDirection axis) { @@ -954,7 +963,12 @@ static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node, } static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) { - return child->style.alignSelf == YGAlignAuto ? node->style.alignItems : child->style.alignSelf; + const YGAlign align = + child->style.alignSelf == YGAlignAuto ? node->style.alignItems : child->style.alignSelf; + if (align == YGAlignBaseline && YGFlexDirectionIsColumn(node->style.flexDirection)) { + return YGAlignFlexStart; + } + return align; } static inline YGDirection YGNodeResolveDirection(const YGNodeRef node, @@ -966,6 +980,40 @@ static inline YGDirection YGNodeResolveDirection(const YGNodeRef node, } } +static float YGBaseline(const YGNodeRef node) { + if (node->baseline != NULL) { + const float baseline = node->baseline(node, node->layout.measuredDimensions[YGDimensionWidth], node->layout.measuredDimensions[YGDimensionHeight]); + YG_ASSERT(!YGFloatIsUndefined(baseline), "Expect custom baseline function to not return NaN") + return baseline; + } + + YGNodeRef baselineChild = NULL; + for (uint32_t i = 0; i < YGNodeGetChildCount(node); i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->lineIndex > 0) { + break; + } + if (child->style.positionType == YGPositionTypeAbsolute) { + continue; + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + baselineChild = child; + break; + } + + if (baselineChild == NULL) { + baselineChild = child; + } + } + + if (baselineChild == NULL) { + return node->layout.measuredDimensions[YGDimensionHeight]; + } + + const float baseline = YGBaseline(baselineChild); + return baseline + baselineChild->layout.position[YGEdgeTop]; +} + static inline YGFlexDirection YGFlexDirectionResolve(const YGFlexDirection flexDirection, const YGDirection direction) { if (direction == YGDirectionRTL) { @@ -991,6 +1039,24 @@ static inline bool YGNodeIsFlex(const YGNodeRef node) { (YGNodeStyleGetFlexGrow(node) != 0 || YGNodeStyleGetFlexShrink(node) != 0)); } +static bool YGIsBaselineLayout(const YGNodeRef node) { + if (YGFlexDirectionIsColumn(node->style.flexDirection)) { + return false; + } + if (node->style.alignItems == YGAlignBaseline) { + return true; + } + for (uint32_t i = 0; i < YGNodeGetChildCount(node); i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->style.positionType == YGPositionTypeRelative && + child->style.alignSelf == YGAlignBaseline) { + return true; + } + } + + return false; +} + static inline float YGNodeDimWithMargin(const YGNodeRef node, const YGFlexDirection axis, const float widthSize) { @@ -1583,8 +1649,6 @@ static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node, // * Margins cannot be specified as 'auto'. They must be specified in terms of // pixel // values, and the default value is 0. -// * The 'baseline' value is not supported for alignItems and alignSelf -// properties. // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must // be // specified as pixel values, not as percentages. @@ -1680,10 +1744,22 @@ static void YGNodelayoutImpl(const YGNodeRef node, const YGDirection direction = YGNodeResolveDirection(node, parentDirection); node->layout.direction = direction; - node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); - node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); - node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); - node->layout.padding[YGEdgeBottom] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); + node->layout.padding[YGEdgeStart] = + YGNodeLeadingPadding(node, + YGFlexDirectionResolve(YGFlexDirectionRow, direction), + parentWidth); + node->layout.padding[YGEdgeEnd] = + YGNodeTrailingPadding(node, + YGFlexDirectionResolve(YGFlexDirectionRow, direction), + parentWidth); + node->layout.padding[YGEdgeTop] = + YGNodeLeadingPadding(node, + YGFlexDirectionResolve(YGFlexDirectionColumn, direction), + parentWidth); + node->layout.padding[YGEdgeBottom] = + YGNodeTrailingPadding(node, + YGFlexDirectionResolve(YGFlexDirectionColumn, direction), + parentWidth); if (node->measure) { YGNodeWithMeasureFuncSetMeasuredDimensions( @@ -2452,7 +2528,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, } // STEP 8: MULTI-LINE CONTENT ALIGNMENT - if (lineCount > 1 && performLayout && !YGFloatIsUndefined(availableInnerCrossDim)) { + if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) && + !YGFloatIsUndefined(availableInnerCrossDim)) { const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; float crossDimLead = 0; @@ -2472,6 +2549,7 @@ static void YGNodelayoutImpl(const YGNodeRef node, break; case YGAlignAuto: case YGAlignFlexStart: + case YGAlignBaseline: break; } @@ -2482,6 +2560,8 @@ static void YGNodelayoutImpl(const YGNodeRef node, // compute the line's height and find the endIndex float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; for (ii = startIndex; ii < childCount; ii++) { const YGNodeRef child = YGNodeListGet(node->children, ii); @@ -2489,12 +2569,22 @@ static void YGNodelayoutImpl(const YGNodeRef node, if (child->lineIndex != i) { break; } - if (YGNodeIsLayoutDimDefined(child, crossAxis)) { lineHeight = fmaxf(lineHeight, child->layout.measuredDimensions[dim[crossAxis]] + YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)); } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + const float ascent = + YGBaseline(child) + + YGNodeLeadingMargin(child, YGFlexDirectionColumn, availableInnerWidth); + const float descent = + child->layout.measuredDimensions[YGDimensionHeight] + + YGNodeMarginForAxis(child, YGFlexDirectionColumn, availableInnerWidth) - ascent; + maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent); + lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } } } endIndex = ii; @@ -2531,6 +2621,12 @@ static void YGNodelayoutImpl(const YGNodeRef node, // (auto) crossAxis dimension. break; } + case YGAlignBaseline: { + child->layout.position[YGEdgeTop] = + currentLead + maxAscentForCurrentLine - YGBaseline(child) + + YGNodeLeadingPosition(child, YGFlexDirectionColumn, availableInnerCrossDim); + break; + } case YGAlignAuto: break; } diff --git a/ReactCommon/yoga/yoga/Yoga.h b/ReactCommon/yoga/yoga/Yoga.h index ba96b9cf16..34a90f5772 100644 --- a/ReactCommon/yoga/yoga/Yoga.h +++ b/ReactCommon/yoga/yoga/Yoga.h @@ -49,6 +49,7 @@ typedef YGSize (*YGMeasureFunc)(YGNodeRef node, YGMeasureMode widthMode, float height, YGMeasureMode heightMode); +typedef float (*YGBaselineFunc)(YGNodeRef node, const float width, const float height); typedef void (*YGPrintFunc)(YGNodeRef node); typedef int (*YGLogger)(YGLogLevel level, const char *format, va_list args); @@ -138,6 +139,7 @@ WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode YG_NODE_PROPERTY(void *, Context, context); YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc); +YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc) YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc); YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout);