Alter layout engine to conform closer to W3C spec

The primary goals of this change are:
  - Better conformance to the W3C flexbox standard (https://www.w3.org/TR/css-flexbox-1/)
    and a clear articulation of the areas where it deviates from the spec.
  - Support for flex-shrink.
  - Conformance with layout effects of "overflow: hidden".

Specifically, here are the limitations of this implementation as compared to the W3C
flexbox standard (this is also documented in Layout.js):
  - Display property is always assumed to be 'flex' except for Text nodes, which
    are assumed to be 'inline-flex'.
  - The 'zIndex' property (or any form of z ordering) is not supported. Nodes are
    stacked in document order.
  - The 'order' property is not supported. The order of flex items is always defined
    by document order.
  - The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'
    and 'hidden' are not supported.
  - The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The
    rarely-used 'wrap-reverse' is not supported.
  - Rather than allowing arbitrary combinations of flexGrow, flexShrink and
    flexBasis, this algorithm supports only the three most common combinations:
      - flex: 0 is equiavlent to flex: 0 0 auto
      - flex: n (where n is a positive value) is equivalent to flex: n 0 0
      - flex: -1 (or any negative value) is equivalent to flex: 0 1 auto
  - Margins cannot be specified as 'auto'. They must be specified in terms of pixel
    values, and the default value is 0.
  - The 'baseline' value is not supported for alignItems and alignSelf properties.
  - Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be
    specified as pixel values, not as percentages.
  - There is no support for calculation of dimensions based on intrinsic aspect ratios
    (e.g. images).
  - There is no support for forced breaks.
  - It does not support vertical inline directions (top-to-bottom or bottom-to-top text).

And here is how the implementation deviates from the standard (this is also documented in
Layout.js):
  - Section 4.5 of the spec indicates that all flex items have a default minimum
    main size. For text blocks, for example, this is the width of the widest word.
    Calculating the minimum width is expensive, so we forego it and assume a default
    minimum main size of 0.
  - Min/Max sizes in the main axis are not honored when resolving flexible lengths.
  - The spec indicates that the default value for 'flexDirection' is 'row', but
    the algorithm below assumes a default of 'column'.
This commit is contained in:
Adam Comella
2016-04-26 16:35:46 -07:00
parent b0d00ad338
commit f3dd51ab97
36 changed files with 9789 additions and 4157 deletions

View File

@@ -9,11 +9,18 @@
function __transpileToCSharpCommon(code) {
return code
.replace(/'abs-layout'/g, '"abs-layout"')
.replace(/'abs-measure'/g, '"abs-measure"')
.replace(/'flex'/g, '"flex"')
.replace(/'measure'/g, '"measure"')
.replace(/'stretch'/g, '"stretch"')
.replace(/undefined/g, 'null')
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
.replace(/CSS_MEASURE_MODE_/g, 'CSSMeasureMode.')
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
.replace(/CSS_OVERFLOW_/g, 'CSSOverflow.')
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
.replace(/css_direction_t/g, 'CSSDirection')
.replace(/css_align_t/g, 'CSSAlign')
@@ -21,6 +28,10 @@ function __transpileToCSharpCommon(code) {
.replace(/css_measure_mode_t/g, 'CSSMeasureMode')
.replace(/css_dim_t/g, 'MeasureOutput')
.replace(/bool/g, 'boolean')
.replace(/style\[CSS_LEFT/g, 'style.position[POSITION_LEFT')
.replace(/style\[CSS_TOP/g, 'style.position[POSITION_TOP')
.replace(/style\[CSS_RIGHT/g, 'style.position[POSITION_RIGHT')
.replace(/style\[CSS_BOTTOM/g, 'style.position[POSITION_BOTTOM')
.replace(/style\[dim/g, 'style.dimensions[dim')
.replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]')
.replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]')
@@ -28,22 +39,24 @@ function __transpileToCSharpCommon(code) {
.replace(/layout\[pos/g, 'layout.position[pos')
.replace(/layout\[leading/g, 'layout.position[leading')
.replace(/layout\[trailing/g, 'layout.position[trailing')
.replace(/layout\[measuredDim/g, 'layout.measuredDimensions[dim')
.replace(/layout\.measuredWidth/g, 'layout.measuredDimensions[DIMENSION_WIDTH]')
.replace(/layout\.measuredHeight/g, 'layout.measuredDimensions[DIMENSION_HEIGHT]')
.replace(/getPositionType\((.+?)\)/g, '$1.style.positionType')
.replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent')
.replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent')
.replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)')
.replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)')
.replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.layout.dimensions[dim[$2]]\) && $1.layout.dimensions[dim[$2]] >= 0.0\)')
.replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '($1.style.dimensions[dim[$2]] >= 0.0)')
.replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '($1.layout.measuredDimensions[dim[$2]] >= 0.0)')
.replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)')
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]')
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)')
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.measuredDimensions[dim[$3]] - ($2.style.positionType == CSSPositionType.Absolute ? 0 : $2.layout.measuredDimensions[dim[$3]]) - $2.layout.position[pos[$3]]')
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex != 0\)')
.replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)')
.replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)')
.replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)')
.replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)')
.replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)')
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.measuredDimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])')
.replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])')
.replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])')
@@ -51,14 +64,18 @@ function __transpileToCSharpCommon(code) {
.replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])')
.replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])')
.replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)')
.replace(/assert\((.+?),\s*'(.+?)'\)/g, 'Assertions.assertCondition($1, "$2")')
.replace(/isUndefined\((.+?)\)/g, 'float.IsNaN\($1\)')
.replace(/getOverflow\((.+?)\)/g, '$1.style.overflow')
.replace(/layoutNodeInternal\((.+?)\)/g, 'layoutNodeInternal(layoutContext, $1)')
.replace(/style\.position\[CSS_/g, 'style.position[POSITION_')
.replace(/\/\*\(c\)!([^*]+)\*\//g, '')
.replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1')
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1')
// additional case conversions
.replace(/(CSSConstants|CSSWrap|CSSJustify|CSSMeasureMode|CSSAlign|CSSPositionType)\.([_A-Z]+)/g,
.replace(/(CSSConstants|CSSWrap|CSSJustify|CSSMeasureMode|CSSAlign|CSSPositionType|CSSOverflow)\.([_A-Z]+)/g,
function(str, match1, match2) {
return match1 + '.' + constantToPascalCase(match2);
});
@@ -139,12 +156,12 @@ var CSharpTranspiler = {
transpileLayoutEngine: function(code) {
return indent(
__transpileToCSharpCommon(code)
.replace(/function\s+layoutNode.*/, '')
.replace('node.style.measure', 'node.measure')
.replace(/\.children\.length/g, '.getChildCount()')
.replace(/node.children\[i\]/g, 'node.getChildAt(i)')
.replace(/node.children\[ii\]/g, 'node.getChildAt(ii)')
.replace(/node.children\[j\]/g, 'node.getChildAt(j)')
.replace(/fmaxf/g, 'Math.Max')
.replace(/fminf/g, 'Math.Min')
.replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages
.replace(/var\/\*([^\/]+)\*\//g, '$1')
.replace(/ === /g, ' == ')

View File

@@ -9,11 +9,18 @@
function __transpileToJavaCommon(code) {
return code
.replace(/'abs-layout'/g, '"abs-layout"')
.replace(/'abs-measure'/g, '"abs-measure"')
.replace(/'flex'/g, '"flex"')
.replace(/'measure'/g, '"measure"')
.replace(/'stretch'/g, '"stretch"')
.replace(/undefined/g, 'null')
.replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED')
.replace(/CSS_JUSTIFY_/g, 'CSSJustify.')
.replace(/CSS_MEASURE_MODE_/g, 'CSSMeasureMode.')
.replace(/CSS_ALIGN_/g, 'CSSAlign.')
.replace(/CSS_POSITION_/g, 'CSSPositionType.')
.replace(/CSS_OVERFLOW_/g, 'CSSOverflow.')
.replace(/css_flex_direction_t/g, 'CSSFlexDirection')
.replace(/css_direction_t/g, 'CSSDirection')
.replace(/css_align_t/g, 'CSSAlign')
@@ -21,6 +28,10 @@ function __transpileToJavaCommon(code) {
.replace(/css_measure_mode_t/g, 'CSSMeasureMode')
.replace(/css_dim_t/g, 'MeasureOutput')
.replace(/bool/g, 'boolean')
.replace(/style\[CSS_LEFT/g, 'style.position[POSITION_LEFT')
.replace(/style\[CSS_TOP/g, 'style.position[POSITION_TOP')
.replace(/style\[CSS_RIGHT/g, 'style.position[POSITION_RIGHT')
.replace(/style\[CSS_BOTTOM/g, 'style.position[POSITION_BOTTOM')
.replace(/style\[dim/g, 'style.dimensions[dim')
.replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]')
.replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]')
@@ -28,22 +39,24 @@ function __transpileToJavaCommon(code) {
.replace(/layout\[pos/g, 'layout.position[pos')
.replace(/layout\[leading/g, 'layout.position[leading')
.replace(/layout\[trailing/g, 'layout.position[trailing')
.replace(/layout\[measuredDim/g, 'layout.measuredDimensions[dim')
.replace(/layout\.measuredWidth/g, 'layout.measuredDimensions[DIMENSION_WIDTH]')
.replace(/layout\.measuredHeight/g, 'layout.measuredDimensions[DIMENSION_HEIGHT]')
.replace(/getPositionType\((.+?)\)/g, '$1.style.positionType')
.replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent')
.replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent')
.replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)')
.replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)')
.replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.layout.dimensions[dim[$2]]\) && $1.layout.dimensions[dim[$2]] >= 0.0\)')
.replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '($1.style.dimensions[dim[$2]] >= 0.0)')
.replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '($1.layout.measuredDimensions[dim[$2]] >= 0.0)')
.replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)')
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]')
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)')
.replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.measuredDimensions[dim[$3]] - ($2.style.positionType == CSSPositionType.ABSOLUTE ? 0 : $2.layout.measuredDimensions[dim[$3]]) - $2.layout.position[pos[$3]]')
.replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex != 0\)')
.replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)')
.replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)')
.replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)')
.replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)')
.replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)')
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.measuredDimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)')
.replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])')
.replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])')
.replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])')
@@ -51,7 +64,11 @@ function __transpileToJavaCommon(code) {
.replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])')
.replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])')
.replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)')
.replace(/assert\((.+?),\s*'(.+?)'\)/g, 'Assertions.assertCondition($1, "$2")')
.replace(/isUndefined\((.+?)\)/g, 'Float.isNaN\($1\)')
.replace(/getOverflow\((.+?)\)/g, '$1.style.overflow')
.replace(/layoutNodeInternal\((.+?)\)/g, 'layoutNodeInternal(layoutContext, $1)')
.replace(/style\.position\[CSS_/g, 'style.position[POSITION_')
.replace(/\/\*\(c\)!([^*]+)\*\//g, '')
.replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1')
.replace(/\/\*\(java\)!([^*]+)\*\//g, '$1');
@@ -118,12 +135,12 @@ var JavaTranspiler = {
transpileLayoutEngine: function(code) {
return indent(
__transpileToJavaCommon(code)
.replace(/function\s+layoutNode.*/, '')
.replace('node.style.measure', 'node.measure')
.replace(/\.children\.length/g, '.getChildCount()')
.replace(/node.children\[i\]/g, 'node.getChildAt(i)')
.replace(/node.children\[ii\]/g, 'node.getChildAt(ii)')
.replace(/node.children\[j\]/g, 'node.getChildAt(j)')
.replace(/fmaxf/g, 'Math.max')
.replace(/fminf/g, 'Math.min')
.replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages
.replace(/var\/\*([^\/]+)\*\//g, '$1')
.replace(/ === /g, ' == ')

View File

@@ -92,6 +92,7 @@ var layoutTestUtils = (function() {
margin: 0;
padding: 0;
min-width: 0;
}
hack to ignore three hundred px width of the body {}
@@ -118,17 +119,24 @@ var layoutTestUtils = (function() {
}
function extractNodes(node) {
var layout = node.layout;
delete node.layout;
var keysToCopy = [
'width',
'height',
'left',
'top'
];
var layout = {};
keysToCopy.forEach(function(key) {
layout[key] = node.layout[key];
});
if (node.children && node.children.length > 0) {
layout.children = node.children.map(extractNodes);
} else {
delete node.children;
}
delete layout.right;
delete layout.bottom;
delete layout.direction;
delete node.layout;
return layout;
}
@@ -183,13 +191,17 @@ var layoutTestUtils = (function() {
function computeDOMLayout(node) {
var body = getIframe().contentDocument.body;
function setStyle(div, name, value) {
div.style['-webkit-' + name] = value;
div.style['webkit' + capitalizeFirst(name)] = value;
div.style[name] = value;
}
function transfer(div, node, name, ext) {
if (name in node.style) {
var value = node.style[name] + (ext || '');
div.style['-webkit-' + name] = value;
div.style['webkit' + capitalizeFirst(name)] = value;
div.style[name] = value;
setStyle(div, name, value);
}
}
@@ -202,7 +214,19 @@ var layoutTestUtils = (function() {
transfer(div, node, type + 'Start' + suffix, 'px');
transfer(div, node, type + 'End' + suffix, 'px');
}
function transferFlex(div, node) {
if ('flex' in node.style) {
var flex = node.style.flex;
var resolvedFlex = (
flex < 0 ? '0 1 auto' :
flex > 0 ? (flex + ' 0 0') :
'0 0 auto'
);
setStyle(div, 'flex', resolvedFlex);
}
}
function renderNode(parent, node) {
var div = document.createElement('div');
transfer(div, node, 'width', 'px');
@@ -220,13 +244,14 @@ var layoutTestUtils = (function() {
transferSpacing(div, node, 'border', 'Width');
transfer(div, node, 'flexDirection');
transfer(div, node, 'direction');
transfer(div, node, 'flex');
transferFlex(div, node);
transfer(div, node, 'flexWrap');
transfer(div, node, 'justifyContent');
transfer(div, node, 'alignSelf');
transfer(div, node, 'alignItems');
transfer(div, node, 'alignContent');
transfer(div, node, 'position');
transfer(div, node, 'overflow');
parent.appendChild(div);
(node.children || []).forEach(function(child) {
renderNode(div, child);

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,11 @@ typedef enum {
CSS_JUSTIFY_SPACE_AROUND
} css_justify_t;
typedef enum {
CSS_OVERFLOW_VISIBLE = 0,
CSS_OVERFLOW_HIDDEN
} css_overflow_t;
// Note: auto is only a valid value for alignSelf. It is NOT a valid value for
// alignItems.
typedef enum {
@@ -79,7 +84,8 @@ typedef enum {
typedef enum {
CSS_MEASURE_MODE_UNDEFINED = 0,
CSS_MEASURE_MODE_EXACTLY,
CSS_MEASURE_MODE_AT_MOST
CSS_MEASURE_MODE_AT_MOST,
CSS_MEASURE_MODE_COUNT
} css_measure_mode_t;
typedef enum {
@@ -87,20 +93,40 @@ typedef enum {
CSS_HEIGHT
} css_dimension_t;
typedef struct {
float available_width;
float available_height;
css_measure_mode_t width_measure_mode;
css_measure_mode_t height_measure_mode;
float computed_width;
float computed_height;
} css_cached_measurement_t;
enum {
// This value was chosen based on empiracle data. Even the most complicated
// layouts should not require more than 16 entries to fit within the cache.
CSS_MAX_CACHED_RESULT_COUNT = 16
};
typedef struct {
float position[4];
float dimensions[2];
css_direction_t direction;
float flex_basis;
// Instead of recomputing the entire layout every single time, we
// cache some information to break early when nothing changed
bool should_update;
float last_requested_dimensions[2];
float last_parent_max_width;
float last_parent_max_height;
float last_dimensions[2];
float last_position[2];
css_direction_t last_direction;
int generation_count;
css_direction_t last_parent_direction;
int next_cached_measurements_index;
css_cached_measurement_t cached_measurements[CSS_MAX_CACHED_RESULT_COUNT];
float measured_dimensions[2];
css_cached_measurement_t cached_layout;
} css_layout_t;
typedef struct {
@@ -116,6 +142,7 @@ typedef struct {
css_align_t align_self;
css_position_type_t position_type;
css_wrap_type_t flex_wrap;
css_overflow_t overflow;
float flex;
float margin[6];
float position[4];
@@ -143,8 +170,7 @@ struct css_node {
int children_count;
int line_index;
css_node_t *next_absolute_child;
css_node_t *next_flex_child;
css_node_t* next_child;
css_dim_t (*measure)(void *context, float width, css_measure_mode_t widthMode, float height, css_measure_mode_t heightMode);
void (*print)(void *context);
@@ -166,12 +192,8 @@ typedef enum {
} css_print_options_t;
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 availableWidth, float availableHeight, css_direction_t parentDirection);
bool isUndefined(float value);
// Function that computes the layout!
void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection);
// Reset the calculated layout values for a given node. You should call this before `layoutNode`.
void resetNodeLayout(css_node_t *node);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1238,7 +1238,7 @@ describe('Layout', function() {
testLayoutAgainstExpectedOnly(
{style: {width: 320, flexDirection: 'column'}, children: [
{style: {measure: measureWithRatio2}},
{style: {height: 100, flexDirection: 'row'}, children: [
{style: {height: 100, flexDirection: 'row', overflow: 'hidden'}, children: [
{style: {measure: measureWithRatio2}},
{style: {measure: measureWithRatio2}}
]},
@@ -1354,14 +1354,16 @@ describe('Layout', function() {
});
it('should layout node with text bounded by grand-parent', function() {
testLayout(
testLayoutAgainstExpectedOnly(
{style: {width: 100, padding: 10, alignSelf: 'flex-start'}, children: [
{style: {margin: 10, alignSelf: 'flex-start'}, children: [
{style: {measure: text(texts.big)}}
]}
]},
{width: 100, height: 40 + textSizes.bigHeight, top: 0, left: 0, children: [
{width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 20, left: 20, children: [
// In the flexbox engine implementation, min width of text is not supported so we max
// out at the amount of available space (60)
{width: Math.min(60, textSizes.bigMinWidth), height: textSizes.bigHeight, top: 20, left: 20, children: [
{width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 0, left: 0}
]}
]}
@@ -2485,6 +2487,307 @@ describe('Layout', function() {
});
});
describe('Layout flex:-1', function() {
// Tests for items with flex:-1 in a container with flexDirection:column
it('should not shrink column node when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100}, children: [
{style: {width: 100, flex: -1}, children: [
{style: {width: 100, height: 25}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 25, top: 0, left: 0, children: [
{width: 100, height: 25, top: 0, left: 0}
]}
]}
);
});
it('should shrink column node when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100}, children: [
{style: {width: 100, flex: -1}, children: [
{style: {width: 100, height: 200}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 200, top: 0, left: 0}
]}
]}
);
});
it('should not shrink column node with siblings when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100}, children: [
{style: {width: 100, height: 25}},
{style: {width: 100, flex: -1}, children: [
{style: {width: 100, height: 30}}
]},
{style: {width: 100, height: 15}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 25, top: 0, left: 0},
{width: 100, height: 30, top: 25, left: 0, children: [
{width: 100, height: 30, top: 0, left: 0}
]},
{width: 100, height: 15, top: 55, left: 0},
]}
);
});
it('should shrink column node with siblings when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100}, children: [
{style: {width: 100, height: 25}},
{style: {width: 100, flex: -1}, children: [
{style: {width: 100, height: 80}}
]},
{style: {width: 100, height: 15}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 25, top: 0, left: 0},
{width: 100, height: 60, top: 25, left: 0, children: [
{width: 100, height: 80, top: 0, left: 0}
]},
{width: 100, height: 15, top: 85, left: 0},
]}
);
});
it('should shrink column nodes proportional to their main size when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100}, children: [
{style: {width: 100, height: 30, flex: -1}},
{style: {width: 100, height: 40}},
{style: {width: 100, height: 50, flex: -1}}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 22.5, top: 0, left: 0},
{width: 100, height: 40, top: 22.5, left: 0},
{width: 100, height: 37.5, top: 62.5, left: 0}
]}
);
});
// Tests for items with flex:-1 and overflow:visible in a container with flexDirection:row
it('should not shrink visible row node when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {height: 100, flex: -1}, children: [
{style: {width: 25, height: 100}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0}
]}
]}
);
});
it('should shrink visible row node when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {height: 100, flex: -1}, children: [
{style: {width: 200, height: 100}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
// width would be 100 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 200, height: 100, top: 0, left: 0}
]}
]}
);
});
it('should not shrink visible row node with siblings when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1}, children: [
{style: {width: 30, height: 100}}
]},
{style: {width: 15, height: 100}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
{width: 30, height: 100, top: 0, left: 25, children: [
{width: 30, height: 100, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 55},
]}
);
});
it('should shrink visible row node with siblings when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1}, children: [
{style: {width: 80, height: 100}}
]},
{style: {width: 15, height: 100}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
// width would be 80 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0
{width: 60, height: 100, top: 0, left: 25, children: [
{width: 80, height: 100, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 85},
]}
);
});
it('should shrink visible row nodes when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 30, height: 100, flex: -1}},
{style: {width: 40, height: 100}},
{style: {width: 50, height: 100, flex: -1}}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
// width would be 30 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0
{width: 22.5, height: 100, top: 0, left: 0},
{width: 40, height: 100, top: 0, left: 22.5},
// width would be 50 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0
{width: 37.5, height: 100, top: 0, left: 62.5}
]}
);
});
// Tests for items with flex:-1 and overflow:hidden in a container with flexDirection:row
it('should not shrink hidden row node when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {height: 100, flex: -1, overflow: 'hidden'}, children: [
{style: {width: 25, height: 100}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0}
]}
]}
);
});
it('should shrink hidden row node when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {height: 100, flex: -1, overflow: 'hidden'}, children: [
{style: {width: 200, height: 100}}
]}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 200, height: 100, top: 0, left: 0}
]}
]}
);
});
it('should not shrink hidden row node with siblings when there is space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1, overflow: 'hidden'}, children: [
{style: {width: 30, height: 100}}
]},
{style: {width: 15, height: 100}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
{width: 30, height: 100, top: 0, left: 25, children: [
{width: 30, height: 100, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 55},
]}
);
});
it('should shrink hidden row node with siblings when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1, overflow: 'hidden'}, children: [
{style: {width: 80, height: 100}}
]},
{style: {width: 15, height: 100}},
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
{width: 60, height: 100, top: 0, left: 25, children: [
{width: 80, height: 100, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 85},
]}
);
});
it('should shrink hidden row nodes proportional to their main size when there is not any space left over', function() {
testLayout(
{style: {width: 100, height: 100, flexDirection: 'row'}, children: [
{style: {width: 30, height: 100, flex: -1, overflow: 'hidden'}},
{style: {width: 40, height: 100}},
{style: {width: 50, height: 100, flex: -1, overflow: 'hidden'}}
]},
{width: 100, height: 100, top: 0, left: 0, children: [
{width: 22.5, height: 100, top: 0, left: 0},
{width: 40, height: 100, top: 0, left: 22.5},
{width: 37.5, height: 100, top: 0, left: 62.5}
]}
);
});
// Tests for items with flex:-1 containing a text node
it('should not shrink text node with siblings when there is space left over', function() {
testLayoutAgainstExpectedOnly(
{style: {width: 213, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1, flexDirection: 'row', alignItems: 'flex-start'}, children: [
{style: {measure: text(texts.big)}}
]},
{style: {width: 15, height: 100}},
]},
{width: 213, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
{width: textSizes.bigWidth, height: 100, top: 0, left: 25, children: [
{width: textSizes.bigWidth, height: textSizes.smallHeight, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 25 + textSizes.bigWidth},
]}
);
});
it('should shrink text node with siblings when there is not any space left over', function() {
testLayout(
{style: {width: 140, height: 100, flexDirection: 'row'}, children: [
{style: {width: 25, height: 100}},
{style: {height: 100, flex: -1, flexDirection: 'row', alignItems: 'flex-start'}, children: [
{style: {flex: -1, measure: text(texts.big)}}
]},
{style: {width: 15, height: 100}},
]},
{width: 140, height: 100, top: 0, left: 0, children: [
{width: 25, height: 100, top: 0, left: 0},
{width: textSizes.bigMinWidth, height: 100, top: 0, left: 25, children: [
{width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 0, left: 0}
]},
{width: 15, height: 100, top: 0, left: 25 + textSizes.bigMinWidth},
]}
);
});
});
describe('Layout alignContent', function() {
it('should layout with alignContent: stretch, and alignItems: flex-start', function() {

File diff suppressed because it is too large Load Diff

View File

@@ -18,5 +18,10 @@ namespace Facebook.CSSLayout
Debug.Assert(v != null);
return v;
}
public static void assertCondition(bool condition, string explanation)
{
Debug.Assert(condition, explanation);
}
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.
*/
namespace Facebook.CSSLayout
{
sealed class CSSCachedMeasurement
{
public float availableWidth;
public float availableHeight;
public CSSMeasureMode? widthMeasureMode = null;
public CSSMeasureMode? heightMeasureMode = null;
public float computedWidth;
public float computedHeight;
}
}

View File

@@ -16,6 +16,10 @@ namespace Facebook.CSSLayout
class CSSLayout
{
// This value was chosen based on empiracle data. Even the most complicated
// layouts should not require more than 16 entries to fit within the cache.
public const int MAX_CACHED_RESULT_COUNT = 16;
public const int POSITION_LEFT = 0;
public const int POSITION_TOP = 1;
public const int POSITION_RIGHT = 2;
@@ -25,12 +29,25 @@ namespace Facebook.CSSLayout
public const int DIMENSION_HEIGHT = 1;
public float[] position = new float[4];
public float[] dimensions = new float[2];
public float[] dimensions = {
CSSConstants.Undefined,
CSSConstants.Undefined
};
public CSSDirection direction = CSSDirection.LTR;
/**
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
*/
public float flexBasis;
public int generationCount;
public CSSDirection? lastParentDirection;
public int nextCachedMeasurementsIndex;
public CSSCachedMeasurement[] cachedMeasurements = new CSSCachedMeasurement[MAX_CACHED_RESULT_COUNT];
public float[] measuredDimensions = {
CSSConstants.Undefined,
CSSConstants.Undefined
};
public CSSCachedMeasurement cachedLayout = new CSSCachedMeasurement();
public void resetResult()
{
@@ -38,17 +55,18 @@ namespace Facebook.CSSLayout
FillArray(dimensions, CSSConstants.Undefined);
direction = CSSDirection.LTR;
}
public void copy(CSSLayout layout)
{
position[POSITION_LEFT] = layout.position[POSITION_LEFT];
position[POSITION_TOP] = layout.position[POSITION_TOP];
position[POSITION_RIGHT] = layout.position[POSITION_RIGHT];
position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM];
dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH];
dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT];
direction = layout.direction;
flexBasis = 0;
generationCount = 0;
lastParentDirection = null;
nextCachedMeasurementsIndex = 0;
measuredDimensions[DIMENSION_WIDTH] = CSSConstants.Undefined;
measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.Undefined;
cachedLayout.widthMeasureMode = null;
cachedLayout.heightMeasureMode = null;
}
public override string ToString()

View File

@@ -22,5 +22,6 @@ namespace Facebook.CSSLayout
{
/*package*/
public MeasureOutput measureOutput = new MeasureOutput();
public int currentGenerationCount;
}
}

View File

@@ -1,4 +1,4 @@
/**
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*

View File

@@ -58,8 +58,7 @@ namespace Facebook.CSSLayout
internal readonly CachedCSSLayout lastLayout = new CachedCSSLayout();
internal int lineIndex = 0;
internal /*package*/ CSSNode nextAbsoluteChild;
internal /*package*/ CSSNode nextFlexChild;
internal /*package*/ CSSNode nextChild;
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
readonly List<CSSNode> mChildren = new List<CSSNode>(4);
@@ -155,7 +154,6 @@ namespace Facebook.CSSLayout
public void CalculateLayout()
{
layout.resetResult();
LayoutEngine.layoutNode(DummyLayoutContext, this, CSSConstants.Undefined, CSSConstants.Undefined, null);
}

View File

@@ -0,0 +1,17 @@
/**
* 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.
*/
namespace Facebook.CSSLayout
{
public enum CSSOverflow
{
Visible,
Hidden
}
}

View File

@@ -22,6 +22,7 @@ namespace Facebook.CSSLayout
public CSSAlign alignSelf = CSSAlign.Auto;
public CSSPositionType positionType = CSSPositionType.Relative;
public CSSWrap flexWrap = CSSWrap.NoWrap;
public CSSOverflow overflow = CSSOverflow.Visible;
public float flex;
public Spacing margin = new Spacing();

View File

@@ -41,14 +41,16 @@
<Compile Include="Assertions.cs" />
<Compile Include="CachedCSSLayout.cs" />
<Compile Include="CSSAlign.cs" />
<Compile Include="CSSCachedMeasurement.cs" />
<Compile Include="CSSConstants.cs" />
<Compile Include="CSSDirection.cs" />
<Compile Include="CSSFlexDirection.cs" />
<Compile Include="CSSJustify.cs" />
<Compile Include="CSSMeasureMode.cs" />
<Compile Include="CSSLayout.cs" />
<Compile Include="CSSLayoutContext.cs" />
<Compile Include="CSSMeasureMode.cs" />
<Compile Include="CSSNode.cs" />
<Compile Include="CSSOverflow.cs" />
<Compile Include="CSSPositionType.cs" />
<Compile Include="CSSStyle.cs" />
<Compile Include="CSSWrap.cs" />
@@ -68,4 +70,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
/**
* 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.
*/
package com.facebook.csslayout;
public class CSSCachedMeasurement {
public float availableWidth;
public float availableHeight;
public CSSMeasureMode widthMeasureMode = null;
public CSSMeasureMode heightMeasureMode = null;
public float computedWidth;
public float computedHeight;
}

View File

@@ -14,6 +14,10 @@ import java.util.Arrays;
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
*/
public class CSSLayout {
// This value was chosen based on empiracle data. Even the most complicated
// layouts should not require more than 16 entries to fit within the cache.
public static final int MAX_CACHED_RESULT_COUNT = 16;
public static final int POSITION_LEFT = 0;
public static final int POSITION_TOP = 1;
public static final int POSITION_RIGHT = 2;
@@ -25,24 +29,38 @@ public class CSSLayout {
public float[] position = new float[4];
public float[] dimensions = new float[2];
public CSSDirection direction = CSSDirection.LTR;
/**
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
*/
public float flexBasis;
public int generationCount;
public CSSDirection lastParentDirection;
public int nextCachedMeasurementsIndex;
public CSSCachedMeasurement[] cachedMeasurements = new CSSCachedMeasurement[MAX_CACHED_RESULT_COUNT];
public float[] measuredDimensions = new float[2];
public CSSCachedMeasurement cachedLayout = new CSSCachedMeasurement();
CSSLayout() {
resetResult();
}
public void resetResult() {
Arrays.fill(position, 0);
Arrays.fill(dimensions, CSSConstants.UNDEFINED);
direction = CSSDirection.LTR;
}
public void copy(CSSLayout layout) {
position[POSITION_LEFT] = layout.position[POSITION_LEFT];
position[POSITION_TOP] = layout.position[POSITION_TOP];
position[POSITION_RIGHT] = layout.position[POSITION_RIGHT];
position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM];
dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH];
dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT];
direction = layout.direction;
flexBasis = 0;
generationCount = 0;
lastParentDirection = null;
nextCachedMeasurementsIndex = 0;
measuredDimensions[DIMENSION_WIDTH] = CSSConstants.UNDEFINED;
measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.UNDEFINED;
cachedLayout.widthMeasureMode = null;
cachedLayout.heightMeasureMode = null;
}
@Override

View File

@@ -17,4 +17,5 @@ package com.facebook.csslayout;
*/
public class CSSLayoutContext {
/*package*/ final MeasureOutput measureOutput = new MeasureOutput();
int currentGenerationCount;
}

View File

@@ -63,9 +63,8 @@ public class CSSNode {
public int lineIndex = 0;
/*package*/ CSSNode nextAbsoluteChild;
/*package*/ CSSNode nextFlexChild;
/*package*/ CSSNode nextChild;
private @Nullable ArrayList<CSSNode> mChildren;
private @Nullable CSSNode mParent;
private @Nullable MeasureFunction mMeasureFunction = null;
@@ -139,7 +138,6 @@ public class CSSNode {
* Performs the actual layout and saves the results in {@link #layout}
*/
public void calculateLayout(CSSLayoutContext layoutContext) {
layout.resetResult();
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, CSSConstants.UNDEFINED, null);
}

View File

@@ -0,0 +1,14 @@
/**
* 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.
*/
package com.facebook.csslayout;
public enum CSSOverflow {
VISIBLE,
HIDDEN,
}

View File

@@ -23,6 +23,7 @@ public class CSSStyle {
public CSSAlign alignSelf;
public CSSPositionType positionType;
public CSSWrap flexWrap;
public CSSOverflow overflow;
public float flex;
public Spacing margin = new Spacing();
@@ -51,6 +52,7 @@ public class CSSStyle {
alignSelf = CSSAlign.AUTO;
positionType = CSSPositionType.RELATIVE;
flexWrap = CSSWrap.NOWRAP;
overflow = CSSOverflow.VISIBLE;
flex = 0f;
margin.reset();;

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@ public class LayoutCachingTest {
root.addChildAt(c0, 0);
root.addChildAt(c1, 1);
c0.addChildAt(c0c0, 0);
root.calculateLayout(layoutContext);
assertTreeHasNewLayout(true, root);
markLayoutAppliedForTree(root);

File diff suppressed because it is too large Load Diff

View File

@@ -39,8 +39,7 @@ global.layoutTestUtils = {
};
global.describe = function(name, cb) {
if (name === 'Layout' ||
name === 'Layout alignContent') {
if (name.toLowerCase().indexOf('javascript only') === -1) {
cb();
}
};
@@ -175,6 +174,10 @@ function printLayout(test) {
'exactly': 'CSS_MEASURE_MODE_EXACTLY',
'at-most': 'CSS_MEASURE_MODE_AT_MOST'
});
addEnum(node, 'overflow', 'overflow', {
'visible': 'CSS_OVERFLOW_VISIBLE',
'hidden': 'CSS_OVERFLOW_HIDDEN'
});
addFloat(node, 'flex', 'flex');
addFloat(node, 'width', 'dimensions[CSS_WIDTH]');
addFloat(node, 'height', 'dimensions[CSS_HEIGHT]');
@@ -256,8 +259,13 @@ function printLayout(test) {
function transpileAnnotatedJStoC(jsCode) {
return jsCode
.replace(/'abs-layout'/g, '"abs-layout"')
.replace(/'abs-measure'/g, '"abs-measure"')
.replace(/'flex'/g, '"flex"')
.replace(/'measure'/g, '"measure"')
.replace(/'stretch'/g, '"stretch"')
.replace('node.style.measure', 'node.measure')
.replace(/null/g, 'NULL')
.replace(/undefined/g, 'NULL')
.replace(/\.children\.length/g, '.children_count')
.replace(/\.width/g, '.dimensions[CSS_WIDTH]')
.replace(/\.height/g, '.dimensions[CSS_HEIGHT]')
@@ -266,23 +274,30 @@ 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(/\.nextFlexChild/g, '.next_flex_child')
.replace(/layout\[dim/g, 'layout.dimensions[dim')
.replace(/\.nextChild/g, '.next_child')
.replace(/\.flexBasis/g, '.flex_basis')
.replace(/layout\[pos/g, 'layout.position[pos')
.replace(/layout\[leading/g, 'layout.position[leading')
.replace(/layout\[trailing/g, 'layout.position[trailing')
.replace(/layout\[measuredDim/g, 'layout.measured_dimensions[dim')
.replace(/layout\.measuredWidth/g, 'layout.measured_dimensions[CSS_WIDTH]')
.replace(/layout\.measuredHeight/g, 'layout.measured_dimensions[CSS_HEIGHT]')
.replace(/style\[dim/g, 'style.dimensions[dim')
.replace(/style\[CSS_LEFT/g, 'style.position[CSS_LEFT')
.replace(/style\[CSS_TOP/g, 'style.position[CSS_TOP')
.replace(/style\[CSS_RIGHT/g, 'style.position[CSS_RIGHT')
.replace(/style\[CSS_BOTTOM/g, 'style.position[CSS_BOTTOM')
.replace(/node.children\[i\]/g, 'node->get_child(node->context, i)')
.replace(/node.children\[ii\]/g, 'node->get_child(node->context, ii)')
.replace(/node.children\[j\]/g, 'node->get_child(node->context, j)')
.replace(/node\./g, 'node->')
.replace(/child\./g, 'child->')
.replace(/parent\./g, 'parent->')
.replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->')
.replace(/currentFlexChild\./g, 'currentFlexChild->')
.replace(/currentRelativeChild\./g, 'currentRelativeChild->')
.replace(/getPositionType\((.+?)\)/g, '$1->style.position_type')
.replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content')
.replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content')
.replace(/assert\((.+?),\s*'(.+?)'\);/g, 'assert($1); // $2')
.replace(/getOverflow\((.+?)\)/g, '$1->style.overflow')
.replace(/var\/\*\(c\)!([^*]+)\*\//g, '$1')
.replace(/var\/\*([^\/]+)\*\//g, '$1')
.replace(/ === /g, ' == ')
@@ -290,8 +305,7 @@ function transpileAnnotatedJStoC(jsCode) {
.replace(/\n {2}/g, '\n')
.replace(/\/\*\(c\)!([^*]+)\*\//g, '$1')
.replace(/\/[*]!([^*]+)[*]\//g, '$1')
.replace(/\/\*\(java\)!([^*]+)\*\//g, '')
.split('\n').slice(1, -1).join('\n');
.replace(/\/\*\(java\)!([^*]+)\*\//g, '');
}
function makeConstDefs() {
@@ -318,14 +332,18 @@ function generateFile(fileName, generatedContent) {
fs.writeFileSync(fileName, content);
}
// Extract the function body by trimming the first ('function layoutNode(...) {') and
// last ('}') lines. Also, start the function body with a blank line so that regexes
// that use \n to match the start of a line will match the actual first line.
var computeLayoutCode = [''].concat(computeLayout.toString().split('\n').slice(1, -1)).join('\n');
var allTestsInC = allTests.map(printLayout);
generateFile(__dirname + '/__tests__/Layout-test.c', allTestsInC.join('\n\n'));
generateFile(__dirname + '/Layout-test-utils.c', makeConstDefs());
generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toString()));
generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayout.toString()));
generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayoutCode));
generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayoutCode));
generateFile(__dirname + '/java/tests/com/facebook/csslayout/TestConstants.java', JavaTranspiler.transpileCConstDefs(makeConstDefs()));
generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC));
generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayout.toString()));
generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayoutCode));
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/TestConstants.cs', CSharpTranspiler.transpileCConstDefs(makeConstDefs()));
generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs', CSharpTranspiler.transpileCTestsArray(allTestsInC));