diff --git a/src/Layout.c b/src/Layout.c index a59d7045..feb4f952 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -636,11 +636,15 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction float totalFlexible = 0; int nonFlexibleChildrenCount = 0; + css_node_t* firstFlexChild = NULL; + css_node_t* currentFlexChild = NULL; + float maxWidth; for (i = startLine; i < childCount; ++i) { child = node->get_child(node->context, i); child->next_absolute_child = NULL; + child->next_flex_child = NULL; // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass @@ -694,6 +698,16 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction flexibleChildrenCount++; 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, which represents // the smallest possible size for the child, to compute the remaining @@ -767,21 +781,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction float baseMainDim; float boundMainDim; - // Iterate over every child in the axis. If the flex share of remaining - // space doesn't meet min/max bounds, remove this child from flex - // calculations. - for (i = startLine; i < endLine; ++i) { - child = node->get_child(node->context, i); - if (isFlex(child)) { - baseMainDim = flexibleMainDim * child->style.flex + - getPaddingAndBorderAxis(child, mainAxis); - boundMainDim = boundAxis(child, mainAxis, baseMainDim); + // 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 -= child->style.flex; - } + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= currentFlexChild->style.flex; } + + currentFlexChild = currentFlexChild->next_flex_child; } flexibleMainDim = remainingMainDim / totalFlexible; @@ -790,31 +803,32 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction 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 (i = startLine; i < endLine; ++i) { - 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]] = boundAxis(child, mainAxis, - flexibleMainDim * child->style.flex + getPaddingAndBorderAxis(child, mainAxis) - ); - maxWidth = CSS_UNDEFINED; - if (isDimDefined(node, resolvedRowAxis)) { - maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } + 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, direction); + 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 diff --git a/src/Layout.h b/src/Layout.h index 820f54c1..535fa734 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -137,6 +137,7 @@ struct css_node { 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); diff --git a/src/Layout.js b/src/Layout.js index ac111e3c..ead03eab 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -504,11 +504,15 @@ var computeLayout = (function() { var/*float*/ totalFlexible = 0; var/*int*/ nonFlexibleChildrenCount = 0; + var/*css_node_t**/ firstFlexChild = null; + var/*css_node_t**/ currentFlexChild = null; + var/*float*/ maxWidth; for (i = startLine; i < childCount; ++i) { child = node.children[i]; child.nextAbsoluteChild = null; + child.nextFlexChild = null; // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass @@ -562,6 +566,16 @@ var computeLayout = (function() { flexibleChildrenCount++; 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.nextFlexChild = 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, which represents // the smallest possible size for the child, to compute the remaining @@ -635,21 +649,20 @@ var computeLayout = (function() { var/*float*/ baseMainDim; var/*float*/ boundMainDim; - // Iterate over every child in the axis. If the flex share of remaining - // space doesn't meet min/max bounds, remove this child from flex - // calculations. - for (i = startLine; i < endLine; ++i) { - child = node.children[i]; - if (isFlex(child)) { - baseMainDim = flexibleMainDim * child.style.flex + - getPaddingAndBorderAxis(child, mainAxis); - boundMainDim = boundAxis(child, mainAxis, baseMainDim); + // 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 -= child.style.flex; - } + if (baseMainDim !== boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= currentFlexChild.style.flex; } + + currentFlexChild = currentFlexChild.nextFlexChild; } flexibleMainDim = remainingMainDim / totalFlexible; @@ -658,31 +671,32 @@ var computeLayout = (function() { 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 (i = startLine; i < endLine; ++i) { - child = node.children[i]; - if (isFlex(child)) { - // At this point we know the final size of the element in the main - // dimension - child.layout[dim[mainAxis]] = boundAxis(child, mainAxis, - flexibleMainDim * child.style.flex + getPaddingAndBorderAxis(child, mainAxis) - ); - maxWidth = CSS_UNDEFINED; - if (isDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } + currentFlexChild = firstFlexChild; + while (currentFlexChild !== null) { + // At this point we know the final size of the element in the main + // dimension + currentFlexChild.layout[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, + flexibleMainDim * currentFlexChild.style.flex + + getPaddingAndBorderAxis(currentFlexChild, mainAxis) + ); - // And we recursively call the layout algorithm for this child - layoutNode(/*(java)!layoutContext, */child, maxWidth, direction); + maxWidth = CSS_UNDEFINED; + if (isDimDefined(node, resolvedRowAxis)) { + maxWidth = node.layout[dim[resolvedRowAxis]] - + paddingAndBorderAxisResolvedRow; + } else if (!isMainRowDirection) { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + paddingAndBorderAxisResolvedRow; } + + // And we recursively call the layout algorithm for this child + layoutNode(/*(java)!layoutContext, */currentFlexChild, maxWidth, direction); + + child = currentFlexChild; + currentFlexChild = currentFlexChild.nextFlexChild; + child.nextFlexChild = null; } // We use justifyContent to figure out how to allocate the remaining diff --git a/src/java/src/com/facebook/csslayout/CSSNode.java b/src/java/src/com/facebook/csslayout/CSSNode.java index bd2ec4a3..85d165e8 100644 --- a/src/java/src/com/facebook/csslayout/CSSNode.java +++ b/src/java/src/com/facebook/csslayout/CSSNode.java @@ -64,6 +64,7 @@ public class CSSNode { public int lineIndex = 0; CSSNode nextAbsoluteChild; + CSSNode nextFlexChild; private @Nullable ArrayList mChildren; private @Nullable CSSNode mParent; diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index 2f40c165..1e867671 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -453,11 +453,15 @@ public class LayoutEngine { float totalFlexible = 0; int nonFlexibleChildrenCount = 0; + CSSNode firstFlexChild = null; + CSSNode currentFlexChild = null; + float maxWidth; for (i = startLine; i < childCount; ++i) { child = node.getChildAt(i); child.nextAbsoluteChild = null; + child.nextFlexChild = null; // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass @@ -511,6 +515,16 @@ public class LayoutEngine { flexibleChildrenCount++; 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.nextFlexChild = 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, which represents // the smallest possible size for the child, to compute the remaining @@ -584,21 +598,20 @@ public class LayoutEngine { float baseMainDim; float boundMainDim; - // Iterate over every child in the axis. If the flex share of remaining - // space doesn't meet min/max bounds, remove this child from flex - // calculations. - for (i = startLine; i < endLine; ++i) { - child = node.getChildAt(i); - if (isFlex(child)) { - baseMainDim = flexibleMainDim * child.style.flex + - getPaddingAndBorderAxis(child, mainAxis); - boundMainDim = boundAxis(child, mainAxis, baseMainDim); + // 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 -= child.style.flex; - } + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= currentFlexChild.style.flex; } + + currentFlexChild = currentFlexChild.nextFlexChild; } flexibleMainDim = remainingMainDim / totalFlexible; @@ -607,31 +620,32 @@ public class LayoutEngine { 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 (i = startLine; i < endLine; ++i) { - child = node.getChildAt(i); - if (isFlex(child)) { - // At this point we know the final size of the element in the main - // dimension - child.layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis, - flexibleMainDim * child.style.flex + getPaddingAndBorderAxis(child, mainAxis) - ); - maxWidth = CSSConstants.UNDEFINED; - if (isDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } + 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(layoutContext, child, maxWidth, direction); + maxWidth = CSSConstants.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(layoutContext, currentFlexChild, maxWidth, direction); + + child = currentFlexChild; + currentFlexChild = currentFlexChild.nextFlexChild; + child.nextFlexChild = null; } // We use justifyContent to figure out how to allocate the remaining diff --git a/src/transpile.js b/src/transpile.js index 4666dbaf..ff0f1922 100644 --- a/src/transpile.js +++ b/src/transpile.js @@ -253,6 +253,7 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]') .replace(/\.lineIndex/g, '.line_index') .replace(/\.nextAbsoluteChild/g, '.next_absolute_child') + .replace(/\.nextFlexChild/g, '.next_flex_child') .replace(/layout\[dim/g, 'layout.dimensions[dim') .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') @@ -264,6 +265,7 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/child\./g, 'child->') .replace(/parent\./g, 'parent->') .replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->') + .replace(/currentFlexChild\./g, 'currentFlexChild->') .replace(/getPositionType\((.+?)\)/g, '$1->style.position_type') .replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content') .replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content')