Add support for flex-grow, flex-shrink, and flex-basis

Reviewed By: lucasr

Differential Revision: D3714520

fbshipit-source-id: 80d3a9a1e2b6f74b863bbe22357f2c9865fa290e
This commit is contained in:
Emil Sjolander 2016-08-15 09:15:05 -07:00 committed by Facebook Github Bot 4
parent 451cc2dee7
commit 0ea4198009
3 changed files with 68 additions and 63 deletions

View File

@ -33,7 +33,7 @@ typedef struct CSSLayout {
float dimensions[2]; float dimensions[2];
CSSDirection direction; CSSDirection direction;
float flexBasis; float computedFlexBasis;
// Instead of recomputing the entire layout every single time, we // Instead of recomputing the entire layout every single time, we
// cache some information to break early when nothing changed // cache some information to break early when nothing changed
@ -57,7 +57,9 @@ typedef struct CSSStyle {
CSSPositionType positionType; CSSPositionType positionType;
CSSWrapType flexWrap; CSSWrapType flexWrap;
CSSOverflow overflow; CSSOverflow overflow;
float flex; float flexGrow;
float flexShrink;
float flexBasis;
float margin[6]; float margin[6];
float position[6]; float position[6];
/** /**

View File

@ -23,8 +23,6 @@ __forceinline const float fmaxf(const float a, const float b) {
#endif #endif
#endif #endif
#define POSITIVE_FLEX_IS_AUTO 0
CSSNodeRef CSSNodeNew() { CSSNodeRef CSSNodeNew() {
CSSNodeRef node = calloc(1, sizeof(CSSNode)); CSSNodeRef node = calloc(1, sizeof(CSSNode));
CSS_ASSERT(node, "Could not allocate memory for node"); CSS_ASSERT(node, "Could not allocate memory for node");
@ -44,6 +42,10 @@ void CSSNodeInit(CSSNodeRef node) {
node->hasNewLayout = true; node->hasNewLayout = true;
node->isDirty = false; node->isDirty = false;
node->style.flexGrow = 0;
node->style.flexShrink = 0;
node->style.flexBasis = CSSUndefined;
node->style.alignItems = CSSAlignStretch; node->style.alignItems = CSSAlignStretch;
node->style.alignContent = CSSAlignFlexStart; node->style.alignContent = CSSAlignFlexStart;
@ -130,6 +132,32 @@ bool CSSNodeIsDirty(CSSNodeRef node) {
return node->isDirty; return node->isDirty;
} }
void CSSNodeStyleSetFlex(CSSNodeRef node, float flex) {
if (CSSValueIsUndefined(flex) || flex == 0) {
CSSNodeStyleSetFlexGrow(node, 0);
CSSNodeStyleSetFlexShrink(node, 0);
CSSNodeStyleSetFlexBasis(node, CSSUndefined);
} else if (flex > 0) {
CSSNodeStyleSetFlexGrow(node, flex);
CSSNodeStyleSetFlexShrink(node, 0);
CSSNodeStyleSetFlexBasis(node, 0);
} else {
CSSNodeStyleSetFlexGrow(node, 0);
CSSNodeStyleSetFlexShrink(node, -flex);
CSSNodeStyleSetFlexBasis(node, CSSUndefined);
}
}
float CSSNodeStyleGetFlex(CSSNodeRef node) {
if (node->style.flexGrow > 0) {
return node->style.flexGrow;
} else if (node->style.flexShrink > 0) {
return -node->style.flexShrink;
}
return 0;
}
#define CSS_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \ #define CSS_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \
void CSSNodeSet##name(CSSNodeRef node, type paramName) { \ void CSSNodeSet##name(CSSNodeRef node, type paramName) { \
node->instanceName = paramName; \ node->instanceName = paramName; \
@ -171,7 +199,9 @@ CSS_NODE_STYLE_PROPERTY_IMPL(CSSAlign, AlignSelf, alignSelf, alignSelf);
CSS_NODE_STYLE_PROPERTY_IMPL(CSSPositionType, PositionType, positionType, positionType); CSS_NODE_STYLE_PROPERTY_IMPL(CSSPositionType, PositionType, positionType, positionType);
CSS_NODE_STYLE_PROPERTY_IMPL(CSSWrapType, FlexWrap, flexWrap, flexWrap); CSS_NODE_STYLE_PROPERTY_IMPL(CSSWrapType, FlexWrap, flexWrap, flexWrap);
CSS_NODE_STYLE_PROPERTY_IMPL(CSSOverflow, Overflow, overflow, overflow); CSS_NODE_STYLE_PROPERTY_IMPL(CSSOverflow, Overflow, overflow, overflow);
CSS_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); CSS_NODE_STYLE_PROPERTY_IMPL(float, FlexGrow, flexGrow, flexGrow);
CSS_NODE_STYLE_PROPERTY_IMPL(float, FlexShrink, flexShrink, flexShrink);
CSS_NODE_STYLE_PROPERTY_IMPL(float, FlexBasis, flexBasis, flexBasis);
CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionLeft, positionLeft, position[CSSPositionLeft]); CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionLeft, positionLeft, position[CSSPositionLeft]);
CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionTop, positionTop, position[CSSPositionTop]); CSS_NODE_STYLE_PROPERTY_IMPL(float, PositionTop, positionTop, position[CSSPositionTop]);
@ -324,7 +354,9 @@ static void print_css_node_rec(CSSNode *node, CSSPrintOptions options, uint32_t
printf("alignSelf: 'stretch', "); printf("alignSelf: 'stretch', ");
} }
print_number_nan("flex", node->style.flex); print_number_nan("flexGrow", node->style.flexGrow);
print_number_nan("flexShrink", node->style.flexShrink);
print_number_nan("flexBasis", node->style.flexBasis);
if (node->style.overflow == CSSOverflowHidden) { if (node->style.overflow == CSSOverflowHidden) {
printf("overflow: 'hidden', "); printf("overflow: 'hidden', ");
@ -432,40 +464,6 @@ static bool isColumnDirection(CSSFlexDirection flexDirection) {
return flexDirection == CSSFlexDirectionColumn || flexDirection == CSSFlexDirectionColumnReverse; return flexDirection == CSSFlexDirectionColumn || flexDirection == CSSFlexDirectionColumnReverse;
} }
static bool isFlexBasisAuto(CSSNode *node) {
#if POSITIVE_FLEX_IS_AUTO
// All flex values are auto.
(void) node;
return true;
#else
// A flex value > 0 implies a basis of zero.
return node->style.flex <= 0;
#endif
}
static float getFlexGrowFactor(CSSNode *node) {
// Flex grow is implied by positive values for flex.
if (node->style.flex > 0) {
return node->style.flex;
}
return 0;
}
static float getFlexShrinkFactor(CSSNode *node) {
#if POSITIVE_FLEX_IS_AUTO
// A flex shrink factor of 1 is implied by non-zero values for flex.
if (node->style.flex != 0) {
return 1;
}
#else
// A flex shrink factor of 1 is implied by negative values for flex.
if (node->style.flex < 0) {
return 1;
}
#endif
return 0;
}
static float getLeadingMargin(CSSNode *node, CSSFlexDirection axis) { static float getLeadingMargin(CSSNode *node, CSSFlexDirection axis) {
if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.margin[CSSPositionStart])) { if (isRowDirection(axis) && !CSSValueIsUndefined(node->style.margin[CSSPositionStart])) {
return node->style.margin[CSSPositionStart]; return node->style.margin[CSSPositionStart];
@ -592,12 +590,9 @@ static CSSFlexDirection getCrossFlexDirection(CSSFlexDirection flexDirection,
} }
} }
static float getFlex(CSSNode *node) {
return node->style.flex;
}
static bool isFlex(CSSNode *node) { static bool isFlex(CSSNode *node) {
return (node->style.positionType == CSSPositionTypeRelative && getFlex(node) != 0); return (node->style.positionType == CSSPositionTypeRelative &&
(node->style.flexGrow != 0 || node->style.flexShrink != 0));
} }
static bool isFlexWrap(CSSNode *node) { static bool isFlexWrap(CSSNode *node) {
@ -1022,15 +1017,18 @@ static void layoutNodeImpl(CSSNode *node,
} else { } else {
if (isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionRow)) { if (isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionRow)) {
// The width is definite, so use that as the flex basis. // The width is definite, so use that as the flex basis.
child->layout.flexBasis = fmaxf(child->style.dimensions[CSSDimensionWidth], child->layout.computedFlexBasis =
getPaddingAndBorderAxis(child, CSSFlexDirectionRow)); fmaxf(child->style.dimensions[CSSDimensionWidth],
getPaddingAndBorderAxis(child, CSSFlexDirectionRow));
} else if (!isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionColumn)) { } else if (!isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionColumn)) {
// The height is definite, so use that as the flex basis. // The height is definite, so use that as the flex basis.
child->layout.flexBasis = fmaxf(child->style.dimensions[CSSDimensionHeight], child->layout.computedFlexBasis =
getPaddingAndBorderAxis(child, CSSFlexDirectionColumn)); fmaxf(child->style.dimensions[CSSDimensionHeight],
} else if (!isFlexBasisAuto(child) && !CSSValueIsUndefined(availableInnerMainDim)) { getPaddingAndBorderAxis(child, CSSFlexDirectionColumn));
// If the basis isn't 'auto', it is assumed to be zero. } else if (!CSSValueIsUndefined(child->style.flexBasis) &&
child->layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); !CSSValueIsUndefined(availableInnerMainDim)) {
child->layout.computedFlexBasis =
fmaxf(child->style.flexBasis, getPaddingAndBorderAxis(child, mainAxis));
} else { } else {
// Compute the flex basis and hypothetical main size (i.e. the clamped // Compute the flex basis and hypothetical main size (i.e. the clamped
// flex basis). // flex basis).
@ -1098,7 +1096,7 @@ static void layoutNodeImpl(CSSNode *node,
false, false,
"measure"); "measure");
child->layout.flexBasis = child->layout.computedFlexBasis =
fmaxf(isMainAxisRow ? child->layout.measuredDimensions[CSSDimensionWidth] fmaxf(isMainAxisRow ? child->layout.measuredDimensions[CSSDimensionWidth]
: child->layout.measuredDimensions[CSSDimensionHeight], : child->layout.measuredDimensions[CSSDimensionHeight],
getPaddingAndBorderAxis(child, mainAxis)); getPaddingAndBorderAxis(child, mainAxis));
@ -1149,7 +1147,7 @@ static void layoutNodeImpl(CSSNode *node,
child->lineIndex = lineCount; child->lineIndex = lineCount;
if (child->style.positionType != CSSPositionTypeAbsolute) { if (child->style.positionType != CSSPositionTypeAbsolute) {
float outerFlexBasis = child->layout.flexBasis + getMarginAxis(child, mainAxis); float outerFlexBasis = child->layout.computedFlexBasis + getMarginAxis(child, mainAxis);
// If this is a multi-line flow and this item pushes us over the // If this is a multi-line flow and this item pushes us over the
// available size, we've // available size, we've
@ -1164,12 +1162,13 @@ static void layoutNodeImpl(CSSNode *node,
itemsOnLine++; itemsOnLine++;
if (isFlex(child)) { if (isFlex(child)) {
totalFlexGrowFactors += getFlexGrowFactor(child); totalFlexGrowFactors += child->style.flexGrow;
// Unlike the grow factor, the shrink factor is scaled relative to the // Unlike the grow factor, the shrink factor is scaled relative to the
// child // child
// dimension. // dimension.
totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child->layout.flexBasis; totalFlexShrinkScaledFactors +=
-child->style.flexShrink * child->layout.computedFlexBasis;
} }
// Store a private linked list of children that need to be layed out. // Store a private linked list of children that need to be layed out.
@ -1252,10 +1251,10 @@ static void layoutNodeImpl(CSSNode *node,
float deltaFlexGrowFactors = 0; float deltaFlexGrowFactors = 0;
currentRelativeChild = firstRelativeChild; currentRelativeChild = firstRelativeChild;
while (currentRelativeChild != NULL) { while (currentRelativeChild != NULL) {
childFlexBasis = currentRelativeChild->layout.flexBasis; childFlexBasis = currentRelativeChild->layout.computedFlexBasis;
if (remainingFreeSpace < 0) { if (remainingFreeSpace < 0) {
flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; flexShrinkScaledFactor = -currentRelativeChild->style.flexShrink * childFlexBasis;
// Is this child able to shrink? // Is this child able to shrink?
if (flexShrinkScaledFactor != 0) { if (flexShrinkScaledFactor != 0) {
@ -1275,7 +1274,7 @@ static void layoutNodeImpl(CSSNode *node,
} }
} }
} else if (remainingFreeSpace > 0) { } else if (remainingFreeSpace > 0) {
flexGrowFactor = getFlexGrowFactor(currentRelativeChild); flexGrowFactor = currentRelativeChild->style.flexGrow;
// Is this child able to grow? // Is this child able to grow?
if (flexGrowFactor != 0) { if (flexGrowFactor != 0) {
@ -1306,11 +1305,11 @@ static void layoutNodeImpl(CSSNode *node,
deltaFreeSpace = 0; deltaFreeSpace = 0;
currentRelativeChild = firstRelativeChild; currentRelativeChild = firstRelativeChild;
while (currentRelativeChild != NULL) { while (currentRelativeChild != NULL) {
childFlexBasis = currentRelativeChild->layout.flexBasis; childFlexBasis = currentRelativeChild->layout.computedFlexBasis;
float updatedMainSize = childFlexBasis; float updatedMainSize = childFlexBasis;
if (remainingFreeSpace < 0) { if (remainingFreeSpace < 0) {
flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; flexShrinkScaledFactor = -currentRelativeChild->style.flexShrink * childFlexBasis;
// Is this child able to shrink? // Is this child able to shrink?
if (flexShrinkScaledFactor != 0) { if (flexShrinkScaledFactor != 0) {
@ -1321,7 +1320,7 @@ static void layoutNodeImpl(CSSNode *node,
flexShrinkScaledFactor); flexShrinkScaledFactor);
} }
} else if (remainingFreeSpace > 0) { } else if (remainingFreeSpace > 0) {
flexGrowFactor = getFlexGrowFactor(currentRelativeChild); flexGrowFactor = currentRelativeChild->style.flexGrow;
// Is this child able to grow? // Is this child able to grow?
if (flexGrowFactor != 0) { if (flexGrowFactor != 0) {
@ -1463,7 +1462,8 @@ static void layoutNodeImpl(CSSNode *node,
// If we skipped the flex step, then we can't rely on the // If we skipped the flex step, then we can't rely on the
// measuredDims because // measuredDims because
// they weren't computed. This means we can't call getDimWithMargin. // they weren't computed. This means we can't call getDimWithMargin.
mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child->layout.flexBasis; mainDim +=
betweenMainDim + getMarginAxis(child, mainAxis) + child->layout.computedFlexBasis;
crossDim = availableInnerCrossDim; crossDim = availableInnerCrossDim;
} else { } else {
// The main dimension is the sum of all the elements dimension plus // The main dimension is the sum of all the elements dimension plus

View File

@ -174,6 +174,9 @@ CSS_NODE_STYLE_PROPERTY(CSSPositionType, PositionType, positionType);
CSS_NODE_STYLE_PROPERTY(CSSWrapType, FlexWrap, flexWrap); CSS_NODE_STYLE_PROPERTY(CSSWrapType, FlexWrap, flexWrap);
CSS_NODE_STYLE_PROPERTY(CSSOverflow, Overflow, overflow); CSS_NODE_STYLE_PROPERTY(CSSOverflow, Overflow, overflow);
CSS_NODE_STYLE_PROPERTY(float, Flex, flex); CSS_NODE_STYLE_PROPERTY(float, Flex, flex);
CSS_NODE_STYLE_PROPERTY(float, FlexGrow, flexGrow);
CSS_NODE_STYLE_PROPERTY(float, FlexShrink, flexShrink);
CSS_NODE_STYLE_PROPERTY(float, FlexBasis, flexBasis);
CSS_NODE_STYLE_PROPERTY(float, PositionLeft, positionLeft); CSS_NODE_STYLE_PROPERTY(float, PositionLeft, positionLeft);
CSS_NODE_STYLE_PROPERTY(float, PositionTop, positionTop); CSS_NODE_STYLE_PROPERTY(float, PositionTop, positionTop);