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

View File

@ -23,8 +23,6 @@ __forceinline const float fmaxf(const float a, const float b) {
#endif
#endif
#define POSITIVE_FLEX_IS_AUTO 0
CSSNodeRef CSSNodeNew() {
CSSNodeRef node = calloc(1, sizeof(CSSNode));
CSS_ASSERT(node, "Could not allocate memory for node");
@ -44,6 +42,10 @@ void CSSNodeInit(CSSNodeRef node) {
node->hasNewLayout = true;
node->isDirty = false;
node->style.flexGrow = 0;
node->style.flexShrink = 0;
node->style.flexBasis = CSSUndefined;
node->style.alignItems = CSSAlignStretch;
node->style.alignContent = CSSAlignFlexStart;
@ -130,6 +132,32 @@ bool CSSNodeIsDirty(CSSNodeRef node) {
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) \
void CSSNodeSet##name(CSSNodeRef node, type 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(CSSWrapType, FlexWrap, flexWrap, flexWrap);
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, PositionTop, positionTop, position[CSSPositionTop]);
@ -324,7 +354,9 @@ static void print_css_node_rec(CSSNode *node, CSSPrintOptions options, uint32_t
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) {
printf("overflow: 'hidden', ");
@ -432,40 +464,6 @@ static bool isColumnDirection(CSSFlexDirection flexDirection) {
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) {
if (isRowDirection(axis) && !CSSValueIsUndefined(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) {
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) {
@ -1022,15 +1017,18 @@ static void layoutNodeImpl(CSSNode *node,
} else {
if (isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionRow)) {
// The width is definite, so use that as the flex basis.
child->layout.flexBasis = fmaxf(child->style.dimensions[CSSDimensionWidth],
getPaddingAndBorderAxis(child, CSSFlexDirectionRow));
child->layout.computedFlexBasis =
fmaxf(child->style.dimensions[CSSDimensionWidth],
getPaddingAndBorderAxis(child, CSSFlexDirectionRow));
} else if (!isMainAxisRow && isStyleDimDefined(child, CSSFlexDirectionColumn)) {
// The height is definite, so use that as the flex basis.
child->layout.flexBasis = fmaxf(child->style.dimensions[CSSDimensionHeight],
getPaddingAndBorderAxis(child, CSSFlexDirectionColumn));
} else if (!isFlexBasisAuto(child) && !CSSValueIsUndefined(availableInnerMainDim)) {
// If the basis isn't 'auto', it is assumed to be zero.
child->layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));
child->layout.computedFlexBasis =
fmaxf(child->style.dimensions[CSSDimensionHeight],
getPaddingAndBorderAxis(child, CSSFlexDirectionColumn));
} else if (!CSSValueIsUndefined(child->style.flexBasis) &&
!CSSValueIsUndefined(availableInnerMainDim)) {
child->layout.computedFlexBasis =
fmaxf(child->style.flexBasis, getPaddingAndBorderAxis(child, mainAxis));
} else {
// Compute the flex basis and hypothetical main size (i.e. the clamped
// flex basis).
@ -1098,7 +1096,7 @@ static void layoutNodeImpl(CSSNode *node,
false,
"measure");
child->layout.flexBasis =
child->layout.computedFlexBasis =
fmaxf(isMainAxisRow ? child->layout.measuredDimensions[CSSDimensionWidth]
: child->layout.measuredDimensions[CSSDimensionHeight],
getPaddingAndBorderAxis(child, mainAxis));
@ -1149,7 +1147,7 @@ static void layoutNodeImpl(CSSNode *node,
child->lineIndex = lineCount;
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
// available size, we've
@ -1164,12 +1162,13 @@ static void layoutNodeImpl(CSSNode *node,
itemsOnLine++;
if (isFlex(child)) {
totalFlexGrowFactors += getFlexGrowFactor(child);
totalFlexGrowFactors += child->style.flexGrow;
// Unlike the grow factor, the shrink factor is scaled relative to the
// child
// 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.
@ -1252,10 +1251,10 @@ static void layoutNodeImpl(CSSNode *node,
float deltaFlexGrowFactors = 0;
currentRelativeChild = firstRelativeChild;
while (currentRelativeChild != NULL) {
childFlexBasis = currentRelativeChild->layout.flexBasis;
childFlexBasis = currentRelativeChild->layout.computedFlexBasis;
if (remainingFreeSpace < 0) {
flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;
flexShrinkScaledFactor = -currentRelativeChild->style.flexShrink * childFlexBasis;
// Is this child able to shrink?
if (flexShrinkScaledFactor != 0) {
@ -1275,7 +1274,7 @@ static void layoutNodeImpl(CSSNode *node,
}
}
} else if (remainingFreeSpace > 0) {
flexGrowFactor = getFlexGrowFactor(currentRelativeChild);
flexGrowFactor = currentRelativeChild->style.flexGrow;
// Is this child able to grow?
if (flexGrowFactor != 0) {
@ -1306,11 +1305,11 @@ static void layoutNodeImpl(CSSNode *node,
deltaFreeSpace = 0;
currentRelativeChild = firstRelativeChild;
while (currentRelativeChild != NULL) {
childFlexBasis = currentRelativeChild->layout.flexBasis;
childFlexBasis = currentRelativeChild->layout.computedFlexBasis;
float updatedMainSize = childFlexBasis;
if (remainingFreeSpace < 0) {
flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;
flexShrinkScaledFactor = -currentRelativeChild->style.flexShrink * childFlexBasis;
// Is this child able to shrink?
if (flexShrinkScaledFactor != 0) {
@ -1321,7 +1320,7 @@ static void layoutNodeImpl(CSSNode *node,
flexShrinkScaledFactor);
}
} else if (remainingFreeSpace > 0) {
flexGrowFactor = getFlexGrowFactor(currentRelativeChild);
flexGrowFactor = currentRelativeChild->style.flexGrow;
// Is this child able to grow?
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
// measuredDims because
// 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;
} else {
// 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(CSSOverflow, Overflow, overflow);
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, PositionTop, positionTop);