diff --git a/src/Layout.c b/src/Layout.c index be0d7d20..a59d7045 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -604,6 +604,9 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction css_node_t* child; css_flex_direction_t axis; + css_node_t* firstAbsoluteChild = NULL; + css_node_t* currentAbsoluteChild = NULL; + float definedMainDim = CSS_UNDEFINED; if (isMainDimDefined) { definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; @@ -637,6 +640,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction for (i = startLine; i < childCount; ++i) { child = node->get_child(node->context, i); + child->next_absolute_child = NULL; + // 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 && @@ -650,6 +655,16 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction 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++) { @@ -1075,40 +1090,40 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction } // Calculate dimensions for absolutely positioned elements - for (i = 0; i < childCount; ++i) { - child = node->get_child(node->context, i); - if (child->style.position_type == 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 (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]] - - getBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + 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) + ); } - for (ii = 0; ii < 2; ii++) { - 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 (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 **/ } diff --git a/src/Layout.h b/src/Layout.h index 54d31589..820f54c1 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -129,18 +129,21 @@ typedef struct { 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_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 diff --git a/src/Layout.js b/src/Layout.js index 957e4455..ac111e3c 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -472,6 +472,9 @@ var computeLayout = (function() { var/*css_node_t**/ child; var/*(c)!css_flex_direction_t*//*(java)!int*/ axis; + var/*css_node_t**/ firstAbsoluteChild = null; + var/*css_node_t**/ currentAbsoluteChild = null; + var/*float*/ definedMainDim = CSS_UNDEFINED; if (isMainDimDefined) { definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain; @@ -505,6 +508,8 @@ var computeLayout = (function() { for (i = startLine; i < childCount; ++i) { child = node.children[i]; + child.nextAbsoluteChild = null; + // 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 && @@ -518,6 +523,16 @@ var computeLayout = (function() { getPaddingAndBorderAxis(child, crossAxis) ); } else if (getPositionType(child) === 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.nextAbsoluteChild = 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++) { @@ -943,40 +958,40 @@ var computeLayout = (function() { } // Calculate dimensions for absolutely positioned elements - for (i = 0; i < childCount; ++i) { - child = node.children[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 (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (!isUndefined(node.layout[dim[axis]]) && - !isDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child.layout[dim[axis]] = fmaxf( - boundAxis(child, axis, node.layout[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + 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[dim[axis]]) && + !isDimDefined(currentAbsoluteChild, axis) && + isPosDefined(currentAbsoluteChild, leading[axis]) && + isPosDefined(currentAbsoluteChild, trailing[axis])) { + currentAbsoluteChild.layout[dim[axis]] = fmaxf( + boundAxis(currentAbsoluteChild, axis, node.layout[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) + ); } - for (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isPosDefined(child, trailing[axis]) && - !isPosDefined(child, leading[axis])) { - child.layout[leading[axis]] = - node.layout[dim[axis]] - - child.layout[dim[axis]] - - getPosition(child, trailing[axis]); - } + + if (isPosDefined(currentAbsoluteChild, trailing[axis]) && + !isPosDefined(currentAbsoluteChild, leading[axis])) { + currentAbsoluteChild.layout[leading[axis]] = + node.layout[dim[axis]] - + currentAbsoluteChild.layout[dim[axis]] - + getPosition(currentAbsoluteChild, trailing[axis]); } } + + child = currentAbsoluteChild; + currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild; + child.nextAbsoluteChild = null; } } diff --git a/src/java/src/com/facebook/csslayout/CSSNode.java b/src/java/src/com/facebook/csslayout/CSSNode.java index 91d7ed83..bd2ec4a3 100644 --- a/src/java/src/com/facebook/csslayout/CSSNode.java +++ b/src/java/src/com/facebook/csslayout/CSSNode.java @@ -63,6 +63,8 @@ public class CSSNode { public int lineIndex = 0; + CSSNode nextAbsoluteChild; + private @Nullable ArrayList mChildren; private @Nullable CSSNode mParent; private @Nullable MeasureFunction mMeasureFunction = null; diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index cc92377d..2f40c165 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -421,6 +421,9 @@ public class LayoutEngine { CSSNode child; int axis; + CSSNode firstAbsoluteChild = null; + CSSNode currentAbsoluteChild = null; + float definedMainDim = CSSConstants.UNDEFINED; if (isMainDimDefined) { definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; @@ -454,6 +457,8 @@ public class LayoutEngine { for (i = startLine; i < childCount; ++i) { child = node.getChildAt(i); + child.nextAbsoluteChild = null; + // Pre-fill cross axis dimensions when the child is using stretch before // we call the recursive layout pass if (getAlignItem(node, child) == CSSAlign.STRETCH && @@ -467,6 +472,16 @@ public class LayoutEngine { getPaddingAndBorderAxis(child, crossAxis) ); } else if (child.style.positionType == CSSPositionType.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.nextAbsoluteChild = 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++) { @@ -892,40 +907,40 @@ public class LayoutEngine { } // Calculate dimensions for absolutely positioned elements - for (i = 0; i < childCount; ++i) { - child = node.getChildAt(i); - if (child.style.positionType == CSSPositionType.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 (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]] = Math.max( - boundAxis(child, axis, node.layout.dimensions[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + 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]] = Math.max( + 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) + ); } - for (ii = 0; ii < 2; ii++) { - 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 (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.nextAbsoluteChild; + child.nextAbsoluteChild = null; } } /** END_GENERATED **/ diff --git a/src/transpile.js b/src/transpile.js index 2f93d154..4666dbaf 100644 --- a/src/transpile.js +++ b/src/transpile.js @@ -243,6 +243,7 @@ function printLayout(test) { function transpileAnnotatedJStoC(jsCode) { return jsCode .replace('node.style.measure', 'node.measure') + .replace(/null/g, 'NULL') .replace(/\.children\.length/g, '.children_count') .replace(/\.width/g, '.dimensions[CSS_WIDTH]') .replace(/\.height/g, '.dimensions[CSS_HEIGHT]') @@ -251,6 +252,7 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/\.minWidth/g, '.minDimensions[CSS_WIDTH]') .replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]') .replace(/\.lineIndex/g, '.line_index') + .replace(/\.nextAbsoluteChild/g, '.next_absolute_child') .replace(/layout\[dim/g, 'layout.dimensions[dim') .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') @@ -261,6 +263,7 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/node\./g, 'node->') .replace(/child\./g, 'child->') .replace(/parent\./g, 'parent->') + .replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->') .replace(/getPositionType\((.+?)\)/g, '$1->style.position_type') .replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content') .replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content')