[ReactNative] update Layout

Summary:
I made some changes to css-layout that changes how layout is
computed for absolutely positioned nodes inside absolutely positioned
parents that have borders/padding. There were also some other changes
made to css-layout that haven't been merged in yet.

@public

Test Plan:
Made a node as described above, saw that the layout is
computed differently than in the browser, updated Layout, saw that the
Layout is not computed correctly.
This commit is contained in:
Andrew Rasmussen 2015-05-04 19:59:25 -07:00
parent 66d2f600dd
commit af921542b5
3 changed files with 175 additions and 92 deletions

View File

@ -1,21 +1,15 @@
/**
* @generated SignedSource<<24fa633b4dd81b7fb40c2b2b0b7c97d0>>
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in from github! !!
* !! !!
* !! 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 !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* WARNING: 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) Run import.sh to copy Layout.* to react-native-github
*/
#include <math.h>
@ -44,6 +38,12 @@ void init_css_node(css_node_t *node) {
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;
@ -249,6 +249,10 @@ static float getPaddingAndBorder(css_node_t *node, int location) {
return getPadding(node, location) + getBorder(node, location);
}
static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) {
return getBorder(node, leading[axis]) + getBorder(node, trailing[axis]);
}
static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) {
return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]);
}
@ -298,7 +302,8 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t 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) {
@ -317,6 +322,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 (axis == CSS_FLEX_DIRECTION_COLUMN) {
min = node->style.minDimensions[CSS_HEIGHT];
max = node->style.maxDimensions[CSS_HEIGHT];
} else if (axis == CSS_FLEX_DIRECTION_ROW) {
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
@ -330,7 +359,7 @@ 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)
);
}
@ -347,6 +376,7 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
/** START_GENERATED **/
css_flex_direction_t mainAxis = getFlexDirection(node);
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
CSS_FLEX_DIRECTION_COLUMN :
@ -385,25 +415,31 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// 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] +
node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
}
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;
}
int i;
int ii;
css_node_t* child;
css_flex_direction_t axis;
// 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);
for (i = 0; i < node->children_count; ++i) {
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 &&
@ -411,27 +447,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
!isDimDefined(child, crossAxis)) {
child->layout.dimensions[dim[crossAxis]] = fmaxf(
node->layout.dimensions[dim[crossAxis]] -
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
getPaddingAndBorderAxis(node, crossAxis) -
getMarginAxis(child, 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;
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(
node->layout.dimensions[dim[axis]] -
getPaddingAndBorderAxis(node, axis) -
getMarginAxis(child, axis) -
getPosition(child, leading[axis]) -
getPosition(child, trailing[axis]),
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)
);
@ -449,11 +485,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// We want to execute the next two loops one per line with flex-wrap
int startLine = 0;
int endLine = 0;
int nextLine = 0;
// int nextOffset = 0;
int alreadyComputedNextLayout = 0;
// We aggregate the total dimensions of the container in those two variables
float linesCrossDim = 0;
float linesMainDim = 0;
while (endLine != node->children_count) {
while (endLine < node->children_count) {
// <Loop A> Layout non flexible children and count children by type
// mainContentDim is accumulation of the dimensions and margin of all the
@ -467,8 +504,10 @@ 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);
float maxWidth;
for (i = startLine; i < node->children_count; ++i) {
child = node->get_child(node->context, i);
float nextContentDim = 0;
// It only makes sense to consider a child flexible if we have a computed
@ -478,26 +517,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
totalFlexible += getFlex(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 = CSS_UNDEFINED;
if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
}
}
// This is the main recursive call. We layout non flexible children.
if (nextLine == 0) {
if (alreadyComputedNextLayout == 0) {
layoutNode(child, maxWidth);
}
@ -513,11 +553,14 @@ 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]]) &&
mainContentDim + nextContentDim > definedMainDim) {
nextLine = i + 1;
mainContentDim + nextContentDim > definedMainDim &&
// If there's only one element, then it's bigger than the content
// and needs its own line
i != startLine) {
alreadyComputedNextLayout = 1;
break;
}
nextLine = 0;
alreadyComputedNextLayout = 0;
mainContentDim += nextContentDim;
endLine = i + 1;
}
@ -542,6 +585,26 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// remaining space
if (flexibleChildrenCount != 0) {
float flexibleMainDim = remainingMainDim / totalFlexible;
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 * getFlex(child) +
getPaddingAndBorderAxis(child, mainAxis);
boundMainDim = boundAxis(child, mainAxis, baseMainDim);
if (baseMainDim != boundMainDim) {
remainingMainDim -= boundMainDim;
totalFlexible -= getFlex(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.
@ -551,21 +614,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// 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);
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]] = flexibleMainDim * getFlex(child) +
getPaddingAndBorderAxis(child, mainAxis);
child->layout.dimensions[dim[mainAxis]] = boundAxis(child, 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 = CSS_UNDEFINED;
if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
} else {
} else if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
@ -580,9 +642,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// space available
} else {
css_justify_t justifyContent = getJustifyContent(node);
if (justifyContent == CSS_JUSTIFY_FLEX_START) {
// Do nothing
} else if (justifyContent == CSS_JUSTIFY_CENTER) {
if (justifyContent == CSS_JUSTIFY_CENTER) {
leadingMainDim = remainingMainDim / 2;
} else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
leadingMainDim = remainingMainDim;
@ -612,8 +672,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
float mainDim = leadingMainDim +
getPaddingAndBorder(node, leading[mainAxis]);
for (int i = startLine; i < endLine; ++i) {
css_node_t* child = node->get_child(node->context, i);
for (i = startLine; i < endLine; ++i) {
child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[mainAxis])) {
@ -638,25 +698,38 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
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));
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
}
}
float containerMainAxis = node->layout.dimensions[dim[mainAxis]];
// 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(containerMainAxis)) {
containerMainAxis = fmaxf(
// We're missing the last padding at this point to get the final
// dimension
boundAxis(node, mainAxis, mainDim + getPaddingAndBorder(node, trailing[mainAxis])),
// We can never assign a width smaller than the padding and borders
getPaddingAndBorderAxis(node, mainAxis)
);
}
float containerCrossAxis = node->layout.dimensions[dim[crossAxis]];
if (isUndefined(node->layout.dimensions[dim[crossAxis]])) {
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),
boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)),
getPaddingAndBorderAxis(node, crossAxis)
);
}
// <Loop D> Position elements in the cross axis
for (int i = startLine; i < endLine; ++i) {
css_node_t* child = node->get_child(node->context, i);
for (i = startLine; i < endLine; ++i) {
child = node->get_child(node->context, i);
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
isPosDefined(child, leading[crossAxis])) {
@ -674,21 +747,19 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// alignSelf (child) in order to determine the position in the cross axis
if (getPositionType(child) == 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)) {
child->layout.dimensions[dim[crossAxis]] = fmaxf(
containerCrossAxis -
boundAxis(child, crossAxis, containerCrossAxis -
getPaddingAndBorderAxis(node, crossAxis) -
getMarginAxis(child, crossAxis),
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 -
@ -719,7 +790,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
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 + getPaddingAndBorder(node, trailing[mainAxis])),
// We can never assign a width smaller than the padding and borders
getPaddingAndBorderAxis(node, mainAxis)
);
@ -730,37 +801,38 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
// 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),
boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
getPaddingAndBorderAxis(node, crossAxis)
);
}
// <Loop E> Calculate dimensions for absolutely positioned elements
for (int i = 0; i < node->children_count; ++i) {
css_node_t* child = node->get_child(node->context, i);
for (i = 0; i < node->children_count; ++i) {
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;
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(
node->layout.dimensions[dim[axis]] -
getPaddingAndBorderAxis(node, axis) -
getMarginAxis(child, axis) -
getPosition(child, leading[axis]) -
getPosition(child, trailing[axis]),
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)
);
}
}
for (int ii = 0; ii < 2; ii++) {
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
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]] =

View File

@ -1,21 +1,15 @@
/**
* @generated SignedSource<<58298c7a8815a8675e970b0347dedfed>>
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* !! This file is a check-in from github! !!
* !! !!
* !! 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 !!
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* WARNING: 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) Run import.sh to copy Layout.* to react-native-github
*/
#ifndef __LAYOUT_H
@ -113,6 +107,8 @@ typedef struct {
float padding[4];
float border[4];
float dimensions[2];
float minDimensions[2];
float maxDimensions[2];
} css_style_t;
typedef struct css_node {

15
React/Layout/import.sh Executable file
View File

@ -0,0 +1,15 @@
LAYOUT_C=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.c`
LAYOUT_H=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.h`
REPLACE_STRING="*
* WARNING: 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) Run import.sh to copy Layout.* to react-native-github
*/"
LAYOUT_C=${LAYOUT_C/\*\//$REPLACE_STRING}
LAYOUT_H=${LAYOUT_H/\*\//$REPLACE_STRING}
echo "$LAYOUT_C" > Layout.c
echo "$LAYOUT_H" > Layout.h