From e665acca4b8839424936b38b686daf2c0f9f7f74 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Sun, 20 Sep 2015 20:33:59 -0700 Subject: [PATCH] pull in github css-layout into fbobjc react native Reviewed By: @vjeux Differential Revision: D2448929 --- React/Layout/Layout.c | 894 +++++++++++++++++++++++++++--------- React/Layout/Layout.h | 50 +- React/Views/RCTShadowView.m | 2 +- 3 files changed, 707 insertions(+), 239 deletions(-) diff --git a/React/Layout/Layout.c b/React/Layout/Layout.c index 11ca898ee..2763b8daa 100644 --- a/React/Layout/Layout.c +++ b/React/Layout/Layout.c @@ -6,7 +6,8 @@ * !! You should not modify this file directly. Instead: !! * !! 1) Go to https://github.com/facebook/css-layout !! * !! 2) Make a pull request and get it merged !! - * !! 3) Execute ./import.sh to pull in the latest version !! + * !! 3) Copy the file from github to here !! + * !! (don't forget to keep this header) !! * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * Copyright (c) 2014, Facebook, Inc. @@ -21,9 +22,23 @@ #include #include #include -#include +// in concatenated header, don't include Layout.h it's already at the top +#ifndef CSS_LAYOUT_IMPLEMENTATION #include "Layout.h" +#endif + +#ifdef _MSC_VER +#include +#define isnan _isnan + +/* define fmaxf if < VC12 */ +#if _MSC_VER < 1800 +__forceinline const float fmaxf(const float a, const float b) { + return (a > b) ? a : b; +} +#endif +#endif bool isUndefined(float value) { return isnan(value); @@ -38,16 +53,33 @@ static bool eq(float a, float b) { void init_css_node(css_node_t *node) { node->style.align_items = CSS_ALIGN_STRETCH; + node->style.align_content = CSS_ALIGN_FLEX_START; + + node->style.direction = CSS_DIRECTION_INHERIT; + node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; // Some of the fields default to undefined and not 0 node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->style.position[CSS_LEFT] = CSS_UNDEFINED; node->style.position[CSS_TOP] = CSS_UNDEFINED; node->style.position[CSS_RIGHT] = CSS_UNDEFINED; node->style.position[CSS_BOTTOM] = CSS_UNDEFINED; + node->style.margin[CSS_START] = CSS_UNDEFINED; + node->style.margin[CSS_END] = CSS_UNDEFINED; + node->style.padding[CSS_START] = CSS_UNDEFINED; + node->style.padding[CSS_END] = CSS_UNDEFINED; + node->style.border[CSS_START] = CSS_UNDEFINED; + node->style.border[CSS_END] = CSS_UNDEFINED; + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; @@ -55,11 +87,12 @@ void init_css_node(css_node_t *node) { node->layout.last_requested_dimensions[CSS_WIDTH] = -1; node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; node->layout.last_parent_max_width = -1; + node->layout.last_direction = (css_direction_t)-1; node->layout.should_update = true; } css_node_t *new_css_node() { - css_node_t *node = calloc(1, sizeof(*node)); + css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); init_css_node(node); return node; } @@ -116,8 +149,14 @@ static void print_css_node_rec( } if (options & CSS_PRINT_STYLE) { - if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { + if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { + printf("flexDirection: 'column', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + printf("flexDirection: 'columnReverse', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { printf("flexDirection: 'row', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + printf("flexDirection: 'rowReverse', "); } if (node->style.justify_content == CSS_JUSTIFY_CENTER) { @@ -138,6 +177,14 @@ static void print_css_node_rec( printf("alignItems: 'stretch', "); } + if (node->style.align_content == CSS_ALIGN_CENTER) { + printf("alignContent: 'center', "); + } else if (node->style.align_content == CSS_ALIGN_FLEX_END) { + printf("alignContent: 'flex-end', "); + } else if (node->style.align_content == CSS_ALIGN_STRETCH) { + printf("alignContent: 'stretch', "); + } + if (node->style.align_self == CSS_ALIGN_FLEX_START) { printf("alignSelf: 'flex-start', "); } else if (node->style.align_self == CSS_ALIGN_CENTER) { @@ -157,6 +204,8 @@ static void print_css_node_rec( print_number_0("marginRight", node->style.margin[CSS_RIGHT]); print_number_0("marginTop", node->style.margin[CSS_TOP]); print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]); + print_number_0("marginStart", node->style.margin[CSS_START]); + print_number_0("marginEnd", node->style.margin[CSS_END]); } if (four_equal(node->style.padding)) { @@ -166,6 +215,8 @@ static void print_css_node_rec( print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); print_number_0("paddingTop", node->style.padding[CSS_TOP]); print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]); + print_number_0("paddingStart", node->style.padding[CSS_START]); + print_number_0("paddingEnd", node->style.padding[CSS_END]); } if (four_equal(node->style.border)) { @@ -175,6 +226,8 @@ static void print_css_node_rec( print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]); print_number_0("borderTopWidth", node->style.border[CSS_TOP]); print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]); + print_number_0("borderStartWidth", node->style.border[CSS_START]); + print_number_0("borderEndWidth", node->style.border[CSS_END]); } print_number_nan("width", node->style.dimensions[CSS_WIDTH]); @@ -207,61 +260,131 @@ void print_css_node(css_node_t *node, css_print_options_t options) { } -static css_position_t leading[2] = { +static css_position_t leading[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, - /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT }; -static css_position_t trailing[2] = { +static css_position_t trailing[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM, - /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT }; -static css_position_t pos[2] = { +static css_position_t pos[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, - /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT }; -static css_dimension_t dim[2] = { +static css_dimension_t dim[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT, - /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH }; - - -static float getMargin(css_node_t *node, int location) { - return node->style.margin[location]; +static bool isRowDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_ROW || + flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE; } -static float getPadding(css_node_t *node, int location) { - if (node->style.padding[location] >= 0) { - return node->style.padding[location]; +static bool isColumnDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_COLUMN || + flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; +} + +static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { + return node->style.margin[CSS_START]; } + + return node->style.margin[leading[axis]]; +} + +static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { + return node->style.margin[CSS_END]; + } + + return node->style.margin[trailing[axis]]; +} + +static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_START]) && + node->style.padding[CSS_START] >= 0) { + return node->style.padding[CSS_START]; + } + + if (node->style.padding[leading[axis]] >= 0) { + return node->style.padding[leading[axis]]; + } + return 0; } -static float getBorder(css_node_t *node, int location) { - if (node->style.border[location] >= 0) { - return node->style.border[location]; +static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_END]) && + node->style.padding[CSS_END] >= 0) { + return node->style.padding[CSS_END]; } + + if (node->style.padding[trailing[axis]] >= 0) { + return node->style.padding[trailing[axis]]; + } + return 0; } -static float getPaddingAndBorder(css_node_t *node, int location) { - return getPadding(node, location) + getBorder(node, location); +static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_START]) && + node->style.border[CSS_START] >= 0) { + return node->style.border[CSS_START]; + } + + if (node->style.border[leading[axis]] >= 0) { + return node->style.border[leading[axis]]; + } + + return 0; +} + +static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_END]) && + node->style.border[CSS_END] >= 0) { + return node->style.border[CSS_END]; + } + + if (node->style.border[trailing[axis]] >= 0) { + return node->style.border[trailing[axis]]; + } + + return 0; +} + +static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); +} + +static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); +} + +static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); } static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { - return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]); + return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { - return getPaddingAndBorder(node, leading[axis]) + getPaddingAndBorder(node, trailing[axis]); -} - -static css_position_type_t getPositionType(css_node_t *node) { - return node->style.position_type; -} - -static css_justify_t getJustifyContent(css_node_t *node) { - return node->style.justify_content; + return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); } static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { @@ -271,17 +394,47 @@ static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { return node->style.align_items; } +static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { + css_direction_t direction = node->style.direction; + + if (direction == CSS_DIRECTION_INHERIT) { + direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR; + } + + return direction; +} + static css_flex_direction_t getFlexDirection(css_node_t *node) { return node->style.flex_direction; } +static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) { + if (direction == CSS_DIRECTION_RTL) { + if (flex_direction == CSS_FLEX_DIRECTION_ROW) { + return CSS_FLEX_DIRECTION_ROW_REVERSE; + } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + return CSS_FLEX_DIRECTION_ROW; + } + } + + return flex_direction; +} + +static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) { + if (isColumnDirection(flex_direction)) { + return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + } else { + return CSS_FLEX_DIRECTION_COLUMN; + } +} + static float getFlex(css_node_t *node) { return node->style.flex; } static bool isFlex(css_node_t *node) { return ( - getPositionType(node) == CSS_POSITION_RELATIVE && + node->style.position_type == CSS_POSITION_RELATIVE && getFlex(node) > 0 ); } @@ -292,12 +445,13 @@ static bool isFlexWrap(css_node_t *node) { static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { return node->layout.dimensions[dim[axis]] + - getMargin(node, leading[axis]) + - getMargin(node, trailing[axis]); + getLeadingMargin(node, axis) + + getTrailingMargin(node, axis); } static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) { - return !isUndefined(node->style.dimensions[dim[axis]]); + float value = node->style.dimensions[dim[axis]]; + return !isUndefined(value) && value > 0.0; } static bool isPosDefined(css_node_t *node, css_position_t position) { @@ -316,6 +470,30 @@ static float getPosition(css_node_t *node, css_position_t position) { return 0; } +static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { + float min = CSS_UNDEFINED; + float max = CSS_UNDEFINED; + + if (isColumnDirection(axis)) { + min = node->style.minDimensions[CSS_HEIGHT]; + max = node->style.maxDimensions[CSS_HEIGHT]; + } else if (isRowDirection(axis)) { + min = node->style.minDimensions[CSS_WIDTH]; + max = node->style.maxDimensions[CSS_WIDTH]; + } + + float boundValue = value; + + if (!isUndefined(max) && max >= 0.0 && boundValue > max) { + boundValue = max; + } + if (!isUndefined(min) && min >= 0.0 && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + // When the user specifically sets a value for width or height static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { // The parent already computed us a width or height. We just skip it @@ -329,11 +507,16 @@ static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { // The dimensions can never be smaller than the padding and border node->layout.dimensions[dim[axis]] = fmaxf( - node->style.dimensions[dim[axis]], + boundAxis(node, axis, node->style.dimensions[dim[axis]]), getPaddingAndBorderAxis(node, axis) ); } +static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { + child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - + child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; + } + // If both left and right are defined, then use left. Otherwise return // +left or -right depending on which is defined. static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { @@ -344,105 +527,102 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { return -getPosition(node, trailing[axis]); } -static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { +static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { /** START_GENERATED **/ - css_flex_direction_t mainAxis = getFlexDirection(node); - css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ? - CSS_FLEX_DIRECTION_COLUMN : - CSS_FLEX_DIRECTION_ROW; + css_direction_t direction = resolveDirection(node, parentDirection); + css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); + css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); + css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); // Handle width and height style attributes setDimensionFromStyle(node, mainAxis); setDimensionFromStyle(node, crossAxis); + // Set the resolved resolution in the node's layout + node->layout.direction = direction; + // The position is set by the parent, but we need to complete it with a // delta composed of the margin and left/top/right/bottom - node->layout.position[leading[mainAxis]] += getMargin(node, leading[mainAxis]) + + node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + getRelativePosition(node, mainAxis); - node->layout.position[leading[crossAxis]] += getMargin(node, leading[crossAxis]) + + node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + getRelativePosition(node, crossAxis); + // Inline immutable values from the target node to avoid excessive method + // invocations during the layout calculation. + int childCount = node->children_count; + float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); + if (isMeasureDefined(node)) { + bool isResolvedRowDimDefined = !isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]); + float width = CSS_UNDEFINED; - if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + if (isDimDefined(node, resolvedRowAxis)) { width = node->style.dimensions[CSS_WIDTH]; - } else if (!isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]])) { - width = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]]; + } else if (isResolvedRowDimDefined) { + width = node->layout.dimensions[dim[resolvedRowAxis]]; } else { width = parentMaxWidth - - getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + getMarginAxis(node, resolvedRowAxis); } - width -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + width -= paddingAndBorderAxisResolvedRow; // We only need to give a dimension for the text if we haven't got any // for it computed yet. It can either be from the style attribute or because // the element is flexible. - bool isRowUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_ROW) && - isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]]); + bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; bool isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); // Let's not measure the text if we already know both dimensions if (isRowUndefined || isColumnUndefined) { - css_dim_t measure_dim = node->measure( + css_dim_t measureDim = node->measure( node->context, + width ); if (isRowUndefined) { - node->layout.dimensions[CSS_WIDTH] = measure_dim.dimensions[CSS_WIDTH] + - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + + paddingAndBorderAxisResolvedRow; } if (isColumnUndefined) { - node->layout.dimensions[CSS_HEIGHT] = measure_dim.dimensions[CSS_HEIGHT] + + node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); } } - return; - } - - // Pre-fill some dimensions straight from the parent - for (int i = 0; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (getAlignItem(node, child) == CSS_ALIGN_STRETCH && - getPositionType(child) == CSS_POSITION_RELATIVE && - !isUndefined(node->layout.dimensions[dim[crossAxis]]) && - !isDimDefined(child, crossAxis)) { - child->layout.dimensions[dim[crossAxis]] = fmaxf( - node->layout.dimensions[dim[crossAxis]] - - getPaddingAndBorderAxis(node, crossAxis) - - getMarginAxis(child, crossAxis), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - } else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (!isUndefined(node->layout.dimensions[dim[axis]]) && - !isDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child->layout.dimensions[dim[axis]] = fmaxf( - node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } - } + if (childCount == 0) { + return; } } + bool isNodeFlexWrap = isFlexWrap(node); + + css_justify_t justifyContent = node->style.justify_content; + + float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); + float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); + float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + bool isMainDimDefined = !isUndefined(node->layout.dimensions[dim[mainAxis]]); + bool isCrossDimDefined = !isUndefined(node->layout.dimensions[dim[crossAxis]]); + bool isMainRowDirection = isRowDirection(mainAxis); + + int i; + int ii; + css_node_t* child; + css_flex_direction_t axis; + + css_node_t* firstAbsoluteChild = NULL; + css_node_t* currentAbsoluteChild = NULL; + float definedMainDim = CSS_UNDEFINED; - if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { - definedMainDim = node->layout.dimensions[dim[mainAxis]] - - getPaddingAndBorderAxis(node, mainAxis); + if (isMainDimDefined) { + definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; } // We want to execute the next two loops one per line with flex-wrap @@ -453,7 +633,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // We aggregate the total dimensions of the container in those two variables float linesCrossDim = 0; float linesMainDim = 0; - while (endLine < node->children_count) { + int linesCount = 0; + while (endLine < childCount) { // Layout non flexible children and count children by type // mainContentDim is accumulation of the dimensions and margin of all the @@ -467,43 +648,129 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { int flexibleChildrenCount = 0; float totalFlexible = 0; int nonFlexibleChildrenCount = 0; - for (int i = startLine; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); + + // Use the line loop to position children in the main axis for as long + // as they are using a simple stacking behaviour. Children that are + // immediately stacked in the initial loop will not be touched again + // in . + bool isSimpleStackMain = + (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) || + (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER); + int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + + // Use the initial line loop to position children in the cross axis for + // as long as they are relatively positioned with alignment STRETCH or + // FLEX_START. Children that are immediately stacked in the initial loop + // will not be touched again in . + bool isSimpleStackCross = true; + int firstComplexCross = childCount; + + css_node_t* firstFlexChild = NULL; + css_node_t* currentFlexChild = NULL; + + float mainDim = leadingPaddingAndBorderMain; + float crossDim = 0; + + float maxWidth; + for (i = startLine; i < childCount; ++i) { + child = node->get_child(node->context, i); + child->line_index = linesCount; + + child->next_absolute_child = NULL; + child->next_flex_child = NULL; + + css_align_t alignItem = getAlignItem(node, child); + + // Pre-fill cross axis dimensions when the child is using stretch before + // we call the recursive layout pass + if (alignItem == CSS_ALIGN_STRETCH && + child->style.position_type == CSS_POSITION_RELATIVE && + isCrossDimDefined && + !isDimDefined(child, crossAxis)) { + child->layout.dimensions[dim[crossAxis]] = fmaxf( + boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - + paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, crossAxis) + ); + } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == NULL) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != NULL) { + currentAbsoluteChild->next_absolute_child = child; + } + currentAbsoluteChild = child; + + // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both + // left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + if (!isUndefined(node->layout.dimensions[dim[axis]]) && + !isDimDefined(child, axis) && + isPosDefined(child, leading[axis]) && + isPosDefined(child, trailing[axis])) { + child->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getPaddingAndBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis])), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, axis) + ); + } + } + } + float nextContentDim = 0; // It only makes sense to consider a child flexible if we have a computed // dimension for the node-> - if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) { + if (isMainDimDefined && isFlex(child)) { flexibleChildrenCount++; - totalFlexible += getFlex(child); + totalFlexible += child->style.flex; + + // Store a private linked list of flexible children so that we can + // efficiently traverse them later. + if (firstFlexChild == NULL) { + firstFlexChild = child; + } + if (currentFlexChild != NULL) { + currentFlexChild->next_flex_child = child; + } + currentFlexChild = child; // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information to compute the - // remaining space. + // border and margin. We'll use this partial information, which represents + // the smallest possible size for the child, to compute the remaining + // available space. nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + getMarginAxis(child, mainAxis); } else { - float maxWidth = CSS_UNDEFINED; - if (mainAxis == CSS_FLEX_DIRECTION_ROW) { - // do nothing - } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { - maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) - - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + maxWidth = CSS_UNDEFINED; + if (!isMainRowDirection) { + if (isDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + paddingAndBorderAxisResolvedRow; + } else { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + paddingAndBorderAxisResolvedRow; + } } // This is the main recursive call. We layout non flexible children. if (alreadyComputedNextLayout == 0) { - layoutNode(child, maxWidth); + layoutNode(child, maxWidth, direction); } // Absolute positioned elements do not take part of the layout, so we // don't use them to compute mainContentDim - if (getPositionType(child) == CSS_POSITION_RELATIVE) { + if (child->style.position_type == CSS_POSITION_RELATIVE) { nonFlexibleChildrenCount++; // At this point we know the final size and margin of the element. nextContentDim = getDimWithMargin(child, mainAxis); @@ -511,8 +778,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { } // The element we are about to add would make us go to the next line - if (isFlexWrap(node) && - !isUndefined(node->layout.dimensions[dim[mainAxis]]) && + if (isNodeFlexWrap && + isMainDimDefined && mainContentDim + nextContentDim > definedMainDim && // If there's only one element, then it's bigger than the content // and needs its own line @@ -521,6 +788,44 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { alreadyComputedNextLayout = 1; break; } + + // Disable simple stacking in the main axis for the current line as + // we found a non-trivial child-> The remaining children will be laid out + // in . + if (isSimpleStackMain && + (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) { + isSimpleStackMain = false; + firstComplexMain = i; + } + + // Disable simple stacking in the cross axis for the current line as + // we found a non-trivial child-> The remaining children will be laid out + // in . + if (isSimpleStackCross && + (child->style.position_type != CSS_POSITION_RELATIVE || + (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) || + isUndefined(child->layout.dimensions[dim[crossAxis]]))) { + isSimpleStackCross = false; + firstComplexCross = i; + } + + if (isSimpleStackMain) { + child->layout.position[pos[mainAxis]] += mainDim; + if (isMainDimDefined) { + setTrailingPosition(node, child, mainAxis); + } + + mainDim += getDimWithMargin(child, mainAxis); + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + } + + if (isSimpleStackCross) { + child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; + if (isCrossDimDefined) { + setTrailingPosition(node, child, crossAxis); + } + } + alreadyComputedNextLayout = 0; mainContentDim += nextContentDim; endLine = i + 1; @@ -536,7 +841,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // The remaining available space that needs to be allocated float remainingMainDim = 0; - if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { + if (isMainDimDefined) { remainingMainDim = definedMainDim - mainContentDim; } else { remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; @@ -546,47 +851,63 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // remaining space if (flexibleChildrenCount != 0) { float flexibleMainDim = remainingMainDim / totalFlexible; + float baseMainDim; + float boundMainDim; + + // If the flex share of remaining space doesn't meet min/max bounds, + // remove this child from flex calculations. + currentFlexChild = firstFlexChild; + while (currentFlexChild != NULL) { + baseMainDim = flexibleMainDim * currentFlexChild->style.flex + + getPaddingAndBorderAxis(currentFlexChild, mainAxis); + boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); + + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= currentFlexChild->style.flex; + } + + currentFlexChild = currentFlexChild->next_flex_child; + } + flexibleMainDim = remainingMainDim / totalFlexible; // The non flexible children can overflow the container, in this case // we should just assume that there is no space available. if (flexibleMainDim < 0) { flexibleMainDim = 0; } - // We iterate over the full array and only apply the action on flexible - // children. This is faster than actually allocating a new array that - // contains only flexible children. - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); - if (isFlex(child)) { - // At this point we know the final size of the element in the main - // dimension - child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) + - getPaddingAndBorderAxis(child, mainAxis); - float maxWidth = CSS_UNDEFINED; - if (mainAxis == CSS_FLEX_DIRECTION_ROW) { - // do nothing - } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { - maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) - - getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); - } + currentFlexChild = firstFlexChild; + while (currentFlexChild != NULL) { + // At this point we know the final size of the element in the main + // dimension + currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, + flexibleMainDim * currentFlexChild->style.flex + + getPaddingAndBorderAxis(currentFlexChild, mainAxis) + ); - // And we recursively call the layout algorithm for this child - layoutNode(child, maxWidth); + maxWidth = CSS_UNDEFINED; + if (isDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + paddingAndBorderAxisResolvedRow; + } else if (!isMainRowDirection) { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + paddingAndBorderAxisResolvedRow; } + + // And we recursively call the layout algorithm for this child + layoutNode(currentFlexChild, maxWidth, direction); + + child = currentFlexChild; + currentFlexChild = currentFlexChild->next_flex_child; + child->next_flex_child = NULL; } // We use justifyContent to figure out how to allocate the remaining // space available - } else { - css_justify_t justifyContent = getJustifyContent(node); - if (justifyContent == CSS_JUSTIFY_FLEX_START) { - // Do nothing - } else if (justifyContent == CSS_JUSTIFY_CENTER) { + } else if (justifyContent != CSS_JUSTIFY_FLEX_START) { + if (justifyContent == CSS_JUSTIFY_CENTER) { leadingMainDim = remainingMainDim / 2; } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { leadingMainDim = remainingMainDim; @@ -612,92 +933,90 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // find their position. In order to do that, we accumulate data in // variables that are also useful to compute the total dimensions of the // container! - float crossDim = 0; - float mainDim = leadingMainDim + - getPaddingAndBorder(node, leading[mainAxis]); + mainDim += leadingMainDim; - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); + for (i = firstComplexMain; i < endLine; ++i) { + child = node->get_child(node->context, i); - if (getPositionType(child) == CSS_POSITION_ABSOLUTE && + if (child->style.position_type == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { // In case the child is position absolute and has left/top being // defined, we override the position to whatever the user said // (and margin/border). child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + - getBorder(node, leading[mainAxis]) + - getMargin(child, leading[mainAxis]); + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); } else { // If the child is position absolute (without top/left) or relative, // we put it at the current accumulated offset. child->layout.position[pos[mainAxis]] += mainDim; - } - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements - // do not take part in that phase. - if (getPositionType(child) == CSS_POSITION_RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + // Define the trailing position accordingly. + if (isMainDimDefined) { + setTrailingPosition(node, child, mainAxis); + } + + // Now that we placed the element, we need to update the variables + // We only need to do that for relative elements. Absolute elements + // do not take part in that phase. + if (child->style.position_type == CSS_POSITION_RELATIVE) { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + } } } float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; - if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { + if (!isCrossDimDefined) { containerCrossAxis = fmaxf( // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise - crossDim + getPaddingAndBorderAxis(node, crossAxis), - getPaddingAndBorderAxis(node, crossAxis) + boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), + paddingAndBorderAxisCross ); } // Position elements in the cross axis + for (i = firstComplexCross; i < endLine; ++i) { + child = node->get_child(node->context, i); - for (int i = startLine; i < endLine; ++i) { - css_node_t* child = node->get_child(node->context, i); - - if (getPositionType(child) == CSS_POSITION_ABSOLUTE && + if (child->style.position_type == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[crossAxis])) { // In case the child is absolutely positionned and has a // top/left/bottom/right being set, we override all the previously // computed positions to set it correctly. child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + - getBorder(node, leading[crossAxis]) + - getMargin(child, leading[crossAxis]); + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); } else { - float leadingCrossDim = getPaddingAndBorder(node, leading[crossAxis]); + float leadingCrossDim = leadingPaddingAndBorderCross; // For a relative children, we're either using alignItems (parent) or // alignSelf (child) in order to determine the position in the cross axis - if (getPositionType(child) == CSS_POSITION_RELATIVE) { + if (child->style.position_type == CSS_POSITION_RELATIVE) { css_align_t alignItem = getAlignItem(node, child); - if (alignItem == CSS_ALIGN_FLEX_START) { - // Do nothing - } else if (alignItem == CSS_ALIGN_STRETCH) { + if (alignItem == CSS_ALIGN_STRETCH) { // You can only stretch if the dimension has not already been set // previously. - if (!isDimDefined(child, crossAxis)) { + if (isUndefined(child->layout.dimensions[dim[crossAxis]])) { child->layout.dimensions[dim[crossAxis]] = fmaxf( - containerCrossAxis - - getPaddingAndBorderAxis(node, crossAxis) - - getMarginAxis(child, crossAxis), + boundAxis(child, crossAxis, containerCrossAxis - + paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), // You never want to go smaller than padding getPaddingAndBorderAxis(child, crossAxis) ); } - } else { + } else if (alignItem != CSS_ALIGN_FLEX_START) { // The remaining space between the parent dimensions+padding and child // dimensions+margin. float remainingCrossDim = containerCrossAxis - - getPaddingAndBorderAxis(node, crossAxis) - - getDimWithMargin(child, crossAxis); + paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); if (alignItem == CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -709,77 +1028,194 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) { // And we apply the position child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; + + // Define the trailing position accordingly. + if (isCrossDimDefined) { + setTrailingPosition(node, child, crossAxis); + } } } linesCrossDim += crossDim; linesMainDim = fmaxf(linesMainDim, mainDim); + linesCount += 1; startLine = endLine; } + // + // + // Note(prenaux): More than one line, we need to layout the crossAxis + // according to alignContent. + // + // Note that we could probably remove and handle the one line case + // here too, but for the moment this is safer since it won't interfere with + // previously working code. + // + // See specs: + // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm + // section 9.4 + // + if (linesCount > 1 && isCrossDimDefined) { + float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - + paddingAndBorderAxisCross; + float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + + css_align_t alignContent = node->style.align_content; + if (alignContent == CSS_ALIGN_FLEX_END) { + currentLead += remainingAlignContentDim; + } else if (alignContent == CSS_ALIGN_CENTER) { + currentLead += remainingAlignContentDim / 2; + } else if (alignContent == CSS_ALIGN_STRETCH) { + if (nodeCrossAxisInnerSize > linesCrossDim) { + crossDimLead = (remainingAlignContentDim / linesCount); + } + } + + int endIndex = 0; + for (i = 0; i < linesCount; ++i) { + int startIndex = endIndex; + + // compute the line's height and find the endIndex + float lineHeight = 0; + for (ii = startIndex; ii < childCount; ++ii) { + child = node->get_child(node->context, ii); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } + if (child->line_index != i) { + break; + } + if (!isUndefined(child->layout.dimensions[dim[crossAxis]])) { + lineHeight = fmaxf( + lineHeight, + child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) + ); + } + } + endIndex = ii; + lineHeight += crossDimLead; + + for (ii = startIndex; ii < endIndex; ++ii) { + child = node->get_child(node->context, ii); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } + + css_align_t alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { + child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { + float childHeight = child->layout.dimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with undefined + // (auto) crossAxis dimension. + } + } + + currentLead += lineHeight; + } + } + + bool needsMainTrailingPos = false; + bool needsCrossTrailingPos = false; + // If the user didn't specify a width or height, and it has not been set // by the container, then we set it via the children. - if (isUndefined(node->layout.dimensions[dim[mainAxis]])) { + if (!isMainDimDefined) { node->layout.dimensions[dim[mainAxis]] = fmaxf( // We're missing the last padding at this point to get the final // dimension - linesMainDim + getPaddingAndBorder(node, trailing[mainAxis]), + boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), // We can never assign a width smaller than the padding and borders - getPaddingAndBorderAxis(node, mainAxis) + paddingAndBorderAxisMain ); + + if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || + mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + needsMainTrailingPos = true; + } } - if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { + if (!isCrossDimDefined) { node->layout.dimensions[dim[crossAxis]] = fmaxf( // For the cross dim, we add both sides at the end because the value // is aggregate via a max function. Intermediate negative values // can mess this computation otherwise - linesCrossDim + getPaddingAndBorderAxis(node, crossAxis), - getPaddingAndBorderAxis(node, crossAxis) + boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), + paddingAndBorderAxisCross ); + + if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || + crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + needsCrossTrailingPos = true; + } } - // Calculate dimensions for absolutely positioned elements + // Set trailing position if necessary + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node->get_child(node->context, i); - for (int i = 0; i < node->children_count; ++i) { - css_node_t* child = node->get_child(node->context, i); - if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (!isUndefined(node->layout.dimensions[dim[axis]]) && - !isDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child->layout.dimensions[dim[axis]] = fmaxf( - node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); } - for (int ii = 0; ii < 2; ii++) { - css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isPosDefined(child, trailing[axis]) && - !isPosDefined(child, leading[axis])) { - child->layout.position[leading[axis]] = - node->layout.dimensions[dim[axis]] - - child->layout.dimensions[dim[axis]] - - getPosition(child, trailing[axis]); - } + + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); } } } + + // Calculate dimensions for absolutely positioned elements + currentAbsoluteChild = firstAbsoluteChild; + while (currentAbsoluteChild != NULL) { + // Pre-fill dimensions when using absolute position and both offsets for + // the axis are defined (either both left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + + if (!isUndefined(node->layout.dimensions[dim[axis]]) && + !isDimDefined(currentAbsoluteChild, axis) && + isPosDefined(currentAbsoluteChild, leading[axis]) && + isPosDefined(currentAbsoluteChild, trailing[axis])) { + currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] - + getBorderAxis(node, axis) - + getMarginAxis(currentAbsoluteChild, axis) - + getPosition(currentAbsoluteChild, leading[axis]) - + getPosition(currentAbsoluteChild, trailing[axis]) + ), + // You never want to go smaller than padding + getPaddingAndBorderAxis(currentAbsoluteChild, axis) + ); + } + + if (isPosDefined(currentAbsoluteChild, trailing[axis]) && + !isPosDefined(currentAbsoluteChild, leading[axis])) { + currentAbsoluteChild->layout.position[leading[axis]] = + node->layout.dimensions[dim[axis]] - + currentAbsoluteChild->layout.dimensions[dim[axis]] - + getPosition(currentAbsoluteChild, trailing[axis]); + } + } + + child = currentAbsoluteChild; + currentAbsoluteChild = currentAbsoluteChild->next_absolute_child; + child->next_absolute_child = NULL; + } /** END_GENERATED **/ } -void layoutNode(css_node_t *node, float parentMaxWidth) { +void layoutNode(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { css_layout_t *layout = &node->layout; + css_direction_t direction = node->style.direction; layout->should_update = true; bool skipLayout = @@ -787,6 +1223,7 @@ void layoutNode(css_node_t *node, float parentMaxWidth) { eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && eq(layout->last_parent_max_width, parentMaxWidth); + eq(layout->last_direction, direction); if (skipLayout) { layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; @@ -797,8 +1234,9 @@ void layoutNode(css_node_t *node, float parentMaxWidth) { layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; layout->last_parent_max_width = parentMaxWidth; + layout->last_direction = direction; - layoutNodeImpl(node, parentMaxWidth); + layoutNodeImpl(node, parentMaxWidth, parentDirection); layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; diff --git a/React/Layout/Layout.h b/React/Layout/Layout.h index 51f72493b..f1d2e3833 100644 --- a/React/Layout/Layout.h +++ b/React/Layout/Layout.h @@ -1,5 +1,4 @@ /** - * @generated SignedSource<<58298c7a8815a8675e970b0347dedfed>> * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * !! This file is a check-in from github! !! @@ -7,7 +6,8 @@ * !! You should not modify this file directly. Instead: !! * !! 1) Go to https://github.com/facebook/css-layout !! * !! 2) Make a pull request and get it merged !! - * !! 3) Execute ./import.sh to pull in the latest version !! + * !! 3) Copy the file from github to here !! + * !! (don't forget to keep this header) !! * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * Copyright (c) 2014, Facebook, Inc. @@ -22,12 +22,29 @@ #define __LAYOUT_H #include +#ifndef __cplusplus #include +#endif + +// Not defined in MSVC++ +#ifndef NAN +static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; +#define NAN (*(const float *)__nan) +#endif + #define CSS_UNDEFINED NAN +typedef enum { + CSS_DIRECTION_INHERIT = 0, + CSS_DIRECTION_LTR, + CSS_DIRECTION_RTL +} css_direction_t; + typedef enum { CSS_FLEX_DIRECTION_COLUMN = 0, - CSS_FLEX_DIRECTION_ROW + CSS_FLEX_DIRECTION_COLUMN_REVERSE, + CSS_FLEX_DIRECTION_ROW, + CSS_FLEX_DIRECTION_ROW_REVERSE } css_flex_direction_t; typedef enum { @@ -65,6 +82,8 @@ typedef enum { CSS_TOP, CSS_RIGHT, CSS_BOTTOM, + CSS_START, + CSS_END, CSS_POSITION_COUNT } css_position_t; @@ -74,8 +93,9 @@ typedef enum { } css_dimension_t; typedef struct { - float position[2]; + float position[4]; float dimensions[2]; + css_direction_t direction; // Instead of recomputing the entire layout every single time, we // cache some information to break early when nothing changed @@ -84,6 +104,7 @@ typedef struct { float last_parent_max_width; float last_dimensions[2]; float last_position[2]; + css_direction_t last_direction; } css_layout_t; typedef struct { @@ -91,14 +112,16 @@ typedef struct { } css_dim_t; typedef struct { + css_direction_t direction; css_flex_direction_t flex_direction; css_justify_t justify_content; + css_align_t align_content; css_align_t align_items; css_align_t align_self; css_position_type_t position_type; css_wrap_type_t flex_wrap; float flex; - float margin[4]; + float margin[6]; float position[4]; /** * You should skip all the rules that contain negative values for the @@ -110,22 +133,29 @@ typedef struct { * {left: -5 ...} * {left: 0 ...} */ - float padding[4]; - float border[4]; + float padding[6]; + float border[6]; float dimensions[2]; + float minDimensions[2]; + float maxDimensions[2]; } css_style_t; -typedef struct css_node { +typedef struct css_node css_node_t; +struct css_node { css_style_t style; css_layout_t layout; int children_count; + int line_index; + + css_node_t* next_absolute_child; + css_node_t* next_flex_child; css_dim_t (*measure)(void *context, float width); void (*print)(void *context); struct css_node* (*get_child)(void *context, int i); bool (*is_dirty)(void *context); void *context; -} css_node_t; +}; // Lifecycle of nodes and children @@ -142,7 +172,7 @@ typedef enum { void print_css_node(css_node_t *node, css_print_options_t options); // Function that computes the layout! -void layoutNode(css_node_t *node, float maxWidth); +void layoutNode(css_node_t *node, float maxWidth, css_direction_t parentDirection); bool isUndefined(float value); #endif diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index f2870e22a..f1b942b2d 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -219,7 +219,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st parentConstraint:(__unused CGSize)parentConstraint { [self fillCSSNode:_cssNode]; - layoutNode(_cssNode, CSS_UNDEFINED); + layoutNode(_cssNode, CSS_UNDEFINED, CSS_DIRECTION_INHERIT); [self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero]; }