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
This commit is contained in:
Lukas Woehrl 2017-01-06 06:51:56 -08:00 committed by Facebook Github Bot
parent 5d6ce0e4ae
commit 7cfb7b8d0f
4 changed files with 116 additions and 15 deletions

View File

@ -17,7 +17,8 @@ public enum YogaAlign {
FLEX_START(1), FLEX_START(1),
CENTER(2), CENTER(2),
FLEX_END(3), FLEX_END(3),
STRETCH(4); STRETCH(4),
BASELINE(5);
private int mIntValue; private int mIntValue;
@ -36,6 +37,7 @@ public enum YogaAlign {
case 2: return CENTER; case 2: return CENTER;
case 3: return FLEX_END; case 3: return FLEX_END;
case 4: return STRETCH; case 4: return STRETCH;
case 5: return BASELINE;
default: throw new IllegalArgumentException("Unkown enum value: " + value); default: throw new IllegalArgumentException("Unkown enum value: " + value);
} }
} }

View File

@ -104,13 +104,14 @@ typedef enum YGExperimentalFeature {
YGExperimentalFeatureWebFlexBasis, YGExperimentalFeatureWebFlexBasis,
} YGExperimentalFeature; } YGExperimentalFeature;
#define YGAlignCount 5 #define YGAlignCount 6
typedef enum YGAlign { typedef enum YGAlign {
YGAlignAuto, YGAlignAuto,
YGAlignFlexStart, YGAlignFlexStart,
YGAlignCenter, YGAlignCenter,
YGAlignFlexEnd, YGAlignFlexEnd,
YGAlignStretch, YGAlignStretch,
YGAlignBaseline,
} YGAlign; } YGAlign;
#define YGUnitCount 3 #define YGUnitCount 3

View File

@ -102,6 +102,7 @@ typedef struct YGNode {
struct YGNode *nextChild; struct YGNode *nextChild;
YGMeasureFunc measure; YGMeasureFunc measure;
YGBaselineFunc baseline;
YGPrintFunc print; YGPrintFunc print;
void *context; void *context;
@ -337,6 +338,14 @@ YGMeasureFunc YGNodeGetMeasureFunc(const YGNodeRef node) {
return node->measure; 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) { 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(child->parent == NULL, "Child already has a parent, it must be removed first.");
YG_ASSERT(node->measure == NULL, YG_ASSERT(node->measure == NULL,
@ -954,7 +963,12 @@ static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node,
} }
static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) { 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, 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, static inline YGFlexDirection YGFlexDirectionResolve(const YGFlexDirection flexDirection,
const YGDirection direction) { const YGDirection direction) {
if (direction == YGDirectionRTL) { if (direction == YGDirectionRTL) {
@ -991,6 +1039,24 @@ static inline bool YGNodeIsFlex(const YGNodeRef node) {
(YGNodeStyleGetFlexGrow(node) != 0 || YGNodeStyleGetFlexShrink(node) != 0)); (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, static inline float YGNodeDimWithMargin(const YGNodeRef node,
const YGFlexDirection axis, const YGFlexDirection axis,
const float widthSize) { 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 // * Margins cannot be specified as 'auto'. They must be specified in terms of
// pixel // pixel
// values, and the default value is 0. // 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 // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must
// be // be
// specified as pixel values, not as percentages. // specified as pixel values, not as percentages.
@ -1680,10 +1744,22 @@ static void YGNodelayoutImpl(const YGNodeRef node,
const YGDirection direction = YGNodeResolveDirection(node, parentDirection); const YGDirection direction = YGNodeResolveDirection(node, parentDirection);
node->layout.direction = direction; node->layout.direction = direction;
node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); node->layout.padding[YGEdgeStart] =
node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); YGNodeLeadingPadding(node,
node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); YGFlexDirectionResolve(YGFlexDirectionRow, direction),
node->layout.padding[YGEdgeBottom] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); 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) { if (node->measure) {
YGNodeWithMeasureFuncSetMeasuredDimensions( YGNodeWithMeasureFuncSetMeasuredDimensions(
@ -2452,7 +2528,8 @@ static void YGNodelayoutImpl(const YGNodeRef node,
} }
// STEP 8: MULTI-LINE CONTENT ALIGNMENT // 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; const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;
float crossDimLead = 0; float crossDimLead = 0;
@ -2472,6 +2549,7 @@ static void YGNodelayoutImpl(const YGNodeRef node,
break; break;
case YGAlignAuto: case YGAlignAuto:
case YGAlignFlexStart: case YGAlignFlexStart:
case YGAlignBaseline:
break; break;
} }
@ -2482,6 +2560,8 @@ static void YGNodelayoutImpl(const YGNodeRef node,
// compute the line's height and find the endIndex // compute the line's height and find the endIndex
float lineHeight = 0; float lineHeight = 0;
float maxAscentForCurrentLine = 0;
float maxDescentForCurrentLine = 0;
for (ii = startIndex; ii < childCount; ii++) { for (ii = startIndex; ii < childCount; ii++) {
const YGNodeRef child = YGNodeListGet(node->children, ii); const YGNodeRef child = YGNodeListGet(node->children, ii);
@ -2489,12 +2569,22 @@ static void YGNodelayoutImpl(const YGNodeRef node,
if (child->lineIndex != i) { if (child->lineIndex != i) {
break; break;
} }
if (YGNodeIsLayoutDimDefined(child, crossAxis)) { if (YGNodeIsLayoutDimDefined(child, crossAxis)) {
lineHeight = fmaxf(lineHeight, lineHeight = fmaxf(lineHeight,
child->layout.measuredDimensions[dim[crossAxis]] + child->layout.measuredDimensions[dim[crossAxis]] +
YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)); 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; endIndex = ii;
@ -2531,6 +2621,12 @@ static void YGNodelayoutImpl(const YGNodeRef node,
// (auto) crossAxis dimension. // (auto) crossAxis dimension.
break; break;
} }
case YGAlignBaseline: {
child->layout.position[YGEdgeTop] =
currentLead + maxAscentForCurrentLine - YGBaseline(child) +
YGNodeLeadingPosition(child, YGFlexDirectionColumn, availableInnerCrossDim);
break;
}
case YGAlignAuto: case YGAlignAuto:
break; break;
} }

View File

@ -49,6 +49,7 @@ typedef YGSize (*YGMeasureFunc)(YGNodeRef node,
YGMeasureMode widthMode, YGMeasureMode widthMode,
float height, float height,
YGMeasureMode heightMode); YGMeasureMode heightMode);
typedef float (*YGBaselineFunc)(YGNodeRef node, const float width, const float height);
typedef void (*YGPrintFunc)(YGNodeRef node); typedef void (*YGPrintFunc)(YGNodeRef node);
typedef int (*YGLogger)(YGLogLevel level, const char *format, va_list args); 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(void *, Context, context);
YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc); YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc);
YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc)
YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc); YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc);
YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout); YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout);