diff --git a/Makefile b/Makefile index f40b9264..736e8f27 100644 --- a/Makefile +++ b/Makefile @@ -6,30 +6,60 @@ # of patent rights can be found in the PATENTS file in the same directory. FILES=src/__tests__/Layout-test.c src/Layout.c src/Layout-test-utils.c +JAVA_LIB_DIR=lib + +ifeq ($(OS),Windows_NT) + C_TEST_EXE=./c_test.exe + ENVSEP=";" + WGET=wget --no-check-certificate + LLDB=gdb +else + C_TEST_EXE=./c_test + ENVSEP=":" + WGET=wget + LLDB=lldb +endif all: c c_test java java_test c: transpile_all +ifeq ($(OS),Windows_NT) c_test: c - @gcc -std=c99 -Werror -Wno-padded $(FILES) -lm && ./a.out - @rm a.out + @cl -nologo -Zi -Tpsrc/__tests__/Layout-test.c -Tpsrc/Layout.c -Tpsrc/Layout-test-utils.c -link -incremental:no -out:"$(C_TEST_EXE)" && "$(C_TEST_EXE)" + @rm "$(C_TEST_EXE)" ./*.obj ./*.pdb -java: transpile_all src/java - @if [ ! -f lib/junit4.jar ]; then mkdir lib/; wget -O lib/junit4.jar http://search.maven.org/remotecontent?filepath=junit/junit/4.10/junit-4.10.jar; fi - @if [ ! -f lib/jsr305.jar ]; then mkdir lib/; wget -O lib/jsr305.jar http://search.maven.org/remotecontent?filepath=net/sourceforge/findbugs/jsr305/1.3.7/jsr305-1.3.7.jar; fi - @if [ ! -f lib/infer-annotations-1.4.jar ]; then mkdir lib/; wget -O lib/infer-annotations-1.4.jar https://github.com/facebook/buck/raw/027ffe2b230c08cad7b340646c6f801bd6dabc78/third-party/java/infer-annotations/infer-annotations-1.4.jar; fi - @javac -cp ./lib/junit4.jar:./lib/jsr305.jar:./lib/infer-annotations-1.4.jar -sourcepath ./src/java/src:./src/java/tests src/java/tests/com/facebook/csslayout/*.java +else +c_test: c + @gcc -std=c99 -Werror -Wno-padded $(FILES) -lm -o "$(C_TEST_EXE)" && "$(C_TEST_EXE)" + @rm "$(C_TEST_EXE)" + +debug: + @gcc -std=c99 -ggdb $(FILES) -lm -o $(C_TEST_EXE) && $(LLDB) $(C_TEST_EXE) + @rm $(C_TEST_EXE) + +endif + +$(JAVA_LIB_DIR): + mkdir $(JAVA_LIB_DIR) + +$(JAVA_LIB_DIR)/junit4.jar: | $(JAVA_LIB_DIR) + $(WGET) -O $(JAVA_LIB_DIR)/junit4.jar http://search.maven.org/remotecontent?filepath=junit/junit/4.10/junit-4.10.jar + +$(JAVA_LIB_DIR)/jsr305.jar: | $(JAVA_LIB_DIR) + $(WGET) -O $(JAVA_LIB_DIR)/jsr305.jar http://search.maven.org/remotecontent?filepath=net/sourceforge/findbugs/jsr305/1.3.7/jsr305-1.3.7.jar + +$(JAVA_LIB_DIR)/infer-annotations-1.4.jar: | $(JAVA_LIB_DIR) + $(WGET) -O $(JAVA_LIB_DIR)/infer-annotations-1.4.jar https://github.com/facebook/buck/raw/027ffe2b230c08cad7b340646c6f801bd6dabc78/third-party/java/infer-annotations/infer-annotations-1.4.jar + +java: transpile_all src/java | $(JAVA_LIB_DIR)/junit4.jar $(JAVA_LIB_DIR)/jsr305.jar $(JAVA_LIB_DIR)/infer-annotations-1.4.jar + @javac -cp ./$(JAVA_LIB_DIR)/junit4.jar$(ENVSEP)./$(JAVA_LIB_DIR)/jsr305.jar$(ENVSEP)./$(JAVA_LIB_DIR)/infer-annotations-1.4.jar -sourcepath ./src/java/src$(ENVSEP)./src/java/tests src/java/tests/com/facebook/csslayout/*.java java_test: java - @java -cp ./src/java/src:./src/java/tests:./lib/junit4.jar:./lib/infer-annotations-1.4.jar org.junit.runner.JUnitCore \ + @java -cp ./src/java/src$(ENVSEP)./src/java/tests$(ENVSEP)./$(JAVA_LIB_DIR)/junit4.jar$(ENVSEP)./$(JAVA_LIB_DIR)/infer-annotations-1.4.jar org.junit.runner.JUnitCore \ com.facebook.csslayout.LayoutEngineTest \ com.facebook.csslayout.LayoutCachingTest \ com.facebook.csslayout.CSSNodeTest transpile_all: ./src/transpile.js @node ./src/transpile.js - -debug: - @gcc -ggdb $(FILES) -lm && lldb ./a.out - @rm a.out diff --git a/src/JavaTranspiler.js b/src/JavaTranspiler.js index 53ccc85d..96a8c763 100644 --- a/src/JavaTranspiler.js +++ b/src/JavaTranspiler.js @@ -115,6 +115,7 @@ var JavaTranspiler = { .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(/fmaxf/g, 'Math.max') .replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages .replace(/var\/\*([^\/]+)\*\//g, '$1') diff --git a/src/Layout-test-utils.c b/src/Layout-test-utils.c index 095f7f17..9c25acb1 100644 --- a/src/Layout-test-utils.c +++ b/src/Layout-test-utils.c @@ -10,12 +10,27 @@ #include "Layout-test-utils.h" #include +#ifdef _MSC_VER +#include +#define isnan _isnan + +/* define fmaxf & fminf if < VC12 */ +#if _MSC_VER < 1800 +__forceinline const float fmaxf(const float a, const float b) { + return (a > b) ? a : b; +} +__forceinline const float fminf(const float a, const float b) { + return (a < b) ? a : b; +} +#endif +#endif + /** START_GENERATED **/ -#define SMALL_WIDTH 34.671875 +#define SMALL_WIDTH 35 #define SMALL_HEIGHT 18 -#define BIG_WIDTH 172.421875 -#define BIG_HEIGHT 36 -#define BIG_MIN_WIDTH 100.4375 +#define BIG_WIDTH 172 +#define BIG_HEIGHT 37 +#define BIG_MIN_WIDTH 100 #define SMALL_TEXT "small" #define LONG_TEXT "loooooooooong with space" /** END_GENERATED **/ @@ -30,7 +45,8 @@ typedef struct failed_test_t { static failed_test_t *failed_test_head = NULL; static failed_test_t *failed_test_tail = NULL; static void add_failed_test(const char *name, css_node_t *style, css_node_t *expected) { - failed_test_t *failed_test = malloc(sizeof(failed_test_t)); + failed_test_t *failed_test = (failed_test_t *)malloc(sizeof(failed_test_t)); + failed_test->next = NULL; failed_test->name = name; failed_test->style = style; failed_test->expected = expected; @@ -65,7 +81,7 @@ static bool are_layout_equal(css_node_t *a, css_node_t *b) { } css_dim_t measure(void *context, float width) { - const char *text = context; + const char *text = (const char *)context; css_dim_t dim; if (width != width) { width = 1000000; @@ -87,8 +103,10 @@ css_dim_t measure(void *context, float width) { return dim; } +static int test_ran_count = 0; void test(const char *name, css_node_t *style, css_node_t *expected_layout) { - layoutNode(style, CSS_UNDEFINED, -1); + ++test_ran_count; + layoutNode(style, CSS_UNDEFINED, (css_direction_t)-1); if (!are_layout_equal(style, expected_layout)) { printf("%sF%s", "\x1B[31m", "\x1B[0m"); @@ -109,12 +127,12 @@ int tests_finished() { printf("%sFAIL%s %s\n", "\x1B[31m", "\x1B[0m", failed_test->name); printf("Input: "); - print_css_node(failed_test->style, CSS_PRINT_STYLE | CSS_PRINT_CHILDREN); + print_css_node(failed_test->style, (css_print_options_t)(CSS_PRINT_STYLE | CSS_PRINT_CHILDREN)); printf("Output: "); - print_css_node(failed_test->style, CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN); + print_css_node(failed_test->style, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN)); printf("Expected: "); - print_css_node(failed_test->expected, CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN); + print_css_node(failed_test->expected, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN)); free_css_node(failed_test->style); free_css_node(failed_test->expected); @@ -131,7 +149,7 @@ int tests_finished() { printf("TESTS FAILED: %d\n", tests_failed); return 1; } else { - printf("ALL TESTS PASSED\n"); + printf("ALL TESTS PASSED: %d tests ran.\n", test_ran_count); return 0; } } diff --git a/src/Layout-test-utils.h b/src/Layout-test-utils.h index 7a04f5a3..73569cce 100644 --- a/src/Layout-test-utils.h +++ b/src/Layout-test-utils.h @@ -9,7 +9,6 @@ #include "Layout.h" #include -#include #include void test(const char *name, css_node_t *style, css_node_t *expected_layout); diff --git a/src/Layout-test-utils.js b/src/Layout-test-utils.js index 69a57668..abaa6e17 100644 --- a/src/Layout-test-utils.js +++ b/src/Layout-test-utils.js @@ -10,6 +10,18 @@ var layoutTestUtils = (function() { + // + // Sets the test cases precision, by default set to 1.0, aka pixel precision + // (assuming the browser does pixel snapping - and that we're ok with being + // 'only' pixel perfect). + // + // Set it to '10' for .1 precision, etc... in theory the browser is doing + // 'pixel' snapping so 1.0 should do, the code is left for clarity... + // + // Set it to undefined to disable and use full precision. + // + var testMeasurePrecision = 1.0; + if (typeof jasmine !== 'undefined') { jasmine.matchersUtil.buildFailureMessage = function () { var args = Array.prototype.slice.call(arguments, 0), @@ -198,6 +210,7 @@ var layoutTestUtils = (function() { transfer(div, node, 'justifyContent'); transfer(div, node, 'alignSelf'); transfer(div, node, 'alignItems'); + transfer(div, node, 'alignContent'); transfer(div, node, 'position'); parent.appendChild(div); (node.children || []).forEach(function(child) { @@ -236,6 +249,27 @@ var layoutTestUtils = (function() { return layout; } + function inplaceRoundNumbersInObject(obj) { + if (!testMeasurePrecision) { + // undefined/0, disables rounding + return; + } + + for (var key in obj) { + if (!obj.hasOwnProperty(key)) { + continue; + } + + var val = obj[key]; + if (typeof val === 'number') { + obj[key] = Math.floor((val * testMeasurePrecision) + 0.5) / testMeasurePrecision; + } + else if (typeof val === 'object') { + inplaceRoundNumbersInObject(val); + } + } + } + function nameLayout(name, layout) { var namedLayout = {name: name}; for (var key in layout) { @@ -346,11 +380,13 @@ var layoutTestUtils = (function() { div.style.display = 'flex'; div.style.flexDirection = 'column'; div.style.alignItems = 'flex-start'; + div.style.alignContent = 'flex-start'; var span = document.createElement('span'); span.style.display = 'flex'; span.style.flexDirection = 'column'; span.style.alignItems = 'flex-start'; + span.style.alignContent = 'flex-start'; span.innerText = text; div.appendChild(span); @@ -372,10 +408,17 @@ var layoutTestUtils = (function() { smallWidth: 34.671875, smallHeight: 18, bigWidth: 172.421875, - bigHeight: 36, + bigHeight: 37, bigMinWidth: 100.4375 }; + // Note(prenaux): Clearly not what I would like, but it seems to be the only + // way :( My guess is that since the font on Windows is + // different than on OSX it has a different size. + if (typeof navigator !== 'undefined' && navigator.userAgent.indexOf("Windows NT") > -1) { + preDefinedTextSizes.bigHeight = 36; + } + var textSizes; if (typeof require === 'function') { textSizes = preDefinedTextSizes; @@ -389,6 +432,11 @@ var layoutTestUtils = (function() { }; } + // round the text sizes so that we dont have to update it for every browser + // update, assumes we're ok with pixel precision + inplaceRoundNumbersInObject(preDefinedTextSizes); + inplaceRoundNumbersInObject(textSizes); + return { texts: texts, textSizes: textSizes, @@ -396,14 +444,28 @@ var layoutTestUtils = (function() { testLayout: function(node, expectedLayout) { var layout = computeCSSLayout(node); var domLayout = computeDOMLayout(node); + inplaceRoundNumbersInObject(layout); + inplaceRoundNumbersInObject(domLayout); + inplaceRoundNumbersInObject(expectedLayout); testNamedLayout('expected-dom', expectedLayout, domLayout); testNamedLayout('layout-dom', layout, domLayout); }, + testLayoutAgainstDomOnly: function(node, expectedLayout) { + var layout = computeCSSLayout(node); + var domLayout = computeDOMLayout(node); + inplaceRoundNumbersInObject(layout); + inplaceRoundNumbersInObject(domLayout); + testNamedLayout('layout-dom', layout, domLayout); + }, testFillNodes: testFillNodes, testExtractNodes: testExtractNodes, testRandomLayout: function(node) { - expect({node: node, layout: computeCSSLayout(node)}) - .toEqual({node: node, layout: computeDOMLayout(node)}); + var layout = computeCSSLayout(node); + var domLayout = computeDOMLayout(node); + inplaceRoundNumbersInObject(layout); + inplaceRoundNumbersInObject(domLayout); + expect({node: node, layout: layout}) + .toEqual({node: node, layout: domLayout}); }, testsFinished: function() { console.log('tests finished!'); diff --git a/src/Layout.c b/src/Layout.c index 4b697d2e..9fa6a29b 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -17,10 +17,14 @@ #ifdef _MSC_VER #include #define isnan _isnan + +/* define fmaxf if < VC12 */ +#if _MSC_VER < 1800 __forceinline const float fmaxf(const float a, const float b) { return (a > b) ? a : b; } #endif +#endif bool isUndefined(float value) { return isnan(value); @@ -35,6 +39,7 @@ static bool eq(float a, float b) { void init_css_node(css_node_t *node) { node->style.align_items = CSS_ALIGN_STRETCH; + node->style.align_content = CSS_ALIGN_FLEX_START; node->style.direction = CSS_DIRECTION_INHERIT; node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; @@ -68,7 +73,7 @@ void init_css_node(css_node_t *node) { node->layout.last_requested_dimensions[CSS_WIDTH] = -1; node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; node->layout.last_parent_max_width = -1; - node->layout.last_direction = -1; + node->layout.last_direction = (css_direction_t)-1; node->layout.should_update = true; } @@ -158,6 +163,14 @@ static void print_css_node_rec( printf("alignItems: 'stretch', "); } + if (node->style.align_content == CSS_ALIGN_CENTER) { + printf("alignContent: 'center', "); + } else if (node->style.align_content == CSS_ALIGN_FLEX_END) { + printf("alignContent: 'flex-end', "); + } else if (node->style.align_content == CSS_ALIGN_STRETCH) { + printf("alignContent: 'stretch', "); + } + if (node->style.align_self == CSS_ALIGN_FLEX_START) { printf("alignSelf: 'flex-start', "); } else if (node->style.align_self == CSS_ALIGN_CENTER) { @@ -368,6 +381,10 @@ static css_justify_t getJustifyContent(css_node_t *node) { return node->style.justify_content; } +static css_align_t getAlignContent(css_node_t *node) { + return node->style.align_content; +} + static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { if (child->style.align_self != CSS_ALIGN_AUTO) { return child->style.align_self; @@ -629,6 +646,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction // We aggregate the total dimensions of the container in those two variables float linesCrossDim = 0; float linesMainDim = 0; + int linesCount = 0; while (endLine < node->children_count) { // Layout non flexible children and count children by type @@ -814,6 +832,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); + child->line_index = linesCount; if (getPositionType(child) == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { @@ -859,7 +878,6 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction } // Position elements in the cross axis - for (i = startLine; i < endLine; ++i) { child = node->get_child(node->context, i); @@ -918,9 +936,92 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction linesCrossDim += crossDim; linesMainDim = fmaxf(linesMainDim, mainDim); + linesCount += 1; startLine = endLine; } + // + // + // Note(prenaux): More than one line, we need to layout the crossAxis + // according to alignContent. + // + // Note that we could probably remove and handle the one line case + // here too, but for the moment this is safer since it won't interfere with + // previously working code. + // + // See specs: + // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm + // section 9.4 + // + if (linesCount > 1 && + !isUndefined(node->layout.dimensions[dim[crossAxis]])) { + float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - + getPaddingAndBorderAxis(node, crossAxis); + float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + + float crossDimLead = 0; + float currentLead = getLeadingPaddingAndBorder(node, crossAxis); + + css_align_t alignContent = getAlignContent(node); + if (alignContent == CSS_ALIGN_FLEX_END) { + currentLead += remainingAlignContentDim; + } else if (alignContent == CSS_ALIGN_CENTER) { + currentLead += remainingAlignContentDim / 2; + } else if (alignContent == CSS_ALIGN_STRETCH) { + if (nodeCrossAxisInnerSize > linesCrossDim) { + crossDimLead = (remainingAlignContentDim / linesCount); + } + } + + int endIndex = 0; + for (i = 0; i < linesCount; ++i) { + int startIndex = endIndex; + + // compute the line's height and find the endIndex + float lineHeight = 0; + for (ii = startIndex; ii < node->children_count; ++ii) { + child = node->get_child(node->context, ii); + if (getPositionType(child) != CSS_POSITION_RELATIVE) { + continue; + } + if (child->line_index != i) { + break; + } + if (!isUndefined(child->layout.dimensions[dim[crossAxis]])) { + lineHeight = fmaxf( + lineHeight, + child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) + ); + } + } + endIndex = ii; + lineHeight += crossDimLead; + + for (ii = startIndex; ii < endIndex; ++ii) { + child = node->get_child(node->context, ii); + if (getPositionType(child) != CSS_POSITION_RELATIVE) { + continue; + } + + css_align_t alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { + child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { + float childHeight = child->layout.dimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with undefined + // (auto) crossAxis dimension. + } + } + + currentLead += lineHeight; + } + } + bool needsMainTrailingPos = false; bool needsCrossTrailingPos = false; @@ -950,8 +1051,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction needsCrossTrailingPos = true; } - // Set trailing position if necessary - + // Set trailing position if necessary if (needsMainTrailingPos || needsCrossTrailingPos) { for (i = 0; i < node->children_count; ++i) { child = node->get_child(node->context, i); @@ -966,8 +1066,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction } } - // Calculate dimensions for absolutely positioned elements - + // Calculate dimensions for absolutely positioned elements for (i = 0; i < node->children_count; ++i) { child = node->get_child(node->context, i); if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { diff --git a/src/Layout.h b/src/Layout.h index 2e737e57..d30d8e79 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -103,6 +103,7 @@ typedef struct { css_direction_t direction; css_flex_direction_t flex_direction; css_justify_t justify_content; + css_align_t align_content; css_align_t align_items; css_align_t align_self; css_position_type_t position_type; @@ -131,6 +132,7 @@ typedef struct css_node { css_style_t style; css_layout_t layout; int children_count; + int line_index; css_dim_t (*measure)(void *context, float width); void (*print)(void *context); diff --git a/src/Layout.js b/src/Layout.js index 300bf2bd..7a367981 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -28,7 +28,7 @@ var computeLayout = (function() { var CSS_ALIGN_FLEX_START = 'flex-start'; var CSS_ALIGN_CENTER = 'center'; - // var CSS_ALIGN_FLEX_END = 'flex-end'; + var CSS_ALIGN_FLEX_END = 'flex-end'; var CSS_ALIGN_STRETCH = 'stretch'; var CSS_POSITION_RELATIVE = 'relative'; @@ -229,6 +229,13 @@ var computeLayout = (function() { return 'flex-start'; } + function getAlignContent(node) { + if ('alignContent' in node.style) { + return node.style.alignContent; + } + return 'flex-start'; + } + function getAlignItem(node, child) { if ('alignSelf' in child.style) { return child.style.alignSelf; @@ -510,6 +517,7 @@ var computeLayout = (function() { // We aggregate the total dimensions of the container in those two variables var/*float*/ linesCrossDim = 0; var/*float*/ linesMainDim = 0; + var/*int*/ linesCount = 0; while (endLine < node.children.length) { // Layout non flexible children and count children by type @@ -695,6 +703,7 @@ var computeLayout = (function() { for (i = startLine; i < endLine; ++i) { child = node.children[i]; + child.lineIndex = linesCount; if (getPositionType(child) === CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { @@ -740,7 +749,6 @@ var computeLayout = (function() { } // Position elements in the cross axis - for (i = startLine; i < endLine; ++i) { child = node.children[i]; @@ -799,9 +807,92 @@ var computeLayout = (function() { linesCrossDim += crossDim; linesMainDim = fmaxf(linesMainDim, mainDim); + linesCount += 1; startLine = endLine; } + // + // + // Note(prenaux): More than one line, we need to layout the crossAxis + // according to alignContent. + // + // Note that we could probably remove and handle the one line case + // here too, but for the moment this is safer since it won't interfere with + // previously working code. + // + // See specs: + // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm + // section 9.4 + // + if (linesCount > 1 && + !isUndefined(node.layout[dim[crossAxis]])) { + var/*float*/ nodeCrossAxisInnerSize = node.layout[dim[crossAxis]] - + getPaddingAndBorderAxis(node, crossAxis); + var/*float*/ remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + + var/*float*/ crossDimLead = 0; + var/*float*/ currentLead = getLeadingPaddingAndBorder(node, crossAxis); + + var/*css_align_t*/ alignContent = getAlignContent(node); + if (alignContent === CSS_ALIGN_FLEX_END) { + currentLead += remainingAlignContentDim; + } else if (alignContent === CSS_ALIGN_CENTER) { + currentLead += remainingAlignContentDim / 2; + } else if (alignContent === CSS_ALIGN_STRETCH) { + if (nodeCrossAxisInnerSize > linesCrossDim) { + crossDimLead = (remainingAlignContentDim / linesCount); + } + } + + var/*int*/ endIndex = 0; + for (i = 0; i < linesCount; ++i) { + var/*int*/ startIndex = endIndex; + + // compute the line's height and find the endIndex + var/*float*/ lineHeight = 0; + for (ii = startIndex; ii < node.children.length; ++ii) { + child = node.children[ii]; + if (getPositionType(child) !== CSS_POSITION_RELATIVE) { + continue; + } + if (child.lineIndex !== i) { + break; + } + if (!isUndefined(child.layout[dim[crossAxis]])) { + lineHeight = fmaxf( + lineHeight, + child.layout[dim[crossAxis]] + getMarginAxis(child, crossAxis) + ); + } + } + endIndex = ii; + lineHeight += crossDimLead; + + for (ii = startIndex; ii < endIndex; ++ii) { + child = node.children[ii]; + if (getPositionType(child) !== CSS_POSITION_RELATIVE) { + continue; + } + + var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem === CSS_ALIGN_FLEX_START) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) { + child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[dim[crossAxis]]; + } else if (alignContentAlignItem === CSS_ALIGN_CENTER) { + var/*float*/ childHeight = child.layout[dim[crossAxis]]; + child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with undefined + // (auto) crossAxis dimension. + } + } + + currentLead += lineHeight; + } + } + var/*bool*/ needsMainTrailingPos = false; var/*bool*/ needsCrossTrailingPos = false; @@ -831,8 +922,7 @@ var computeLayout = (function() { needsCrossTrailingPos = true; } - // Set trailing position if necessary - + // Set trailing position if necessary if (needsMainTrailingPos || needsCrossTrailingPos) { for (i = 0; i < node.children.length; ++i) { child = node.children[i]; @@ -847,8 +937,7 @@ var computeLayout = (function() { } } - // Calculate dimensions for absolutely positioned elements - + // Calculate dimensions for absolutely positioned elements for (i = 0; i < node.children.length; ++i) { child = node.children[i]; if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { diff --git a/src/__tests__/Layout-random-test.js b/src/__tests__/Layout-random-test.js index e552cdbe..e974ef19 100644 --- a/src/__tests__/Layout-random-test.js +++ b/src/__tests__/Layout-random-test.js @@ -65,6 +65,7 @@ describe('Random layout', function() { randEnum(node, 0.5, 'justifyContent', ['flex-start', 'center', 'flex-end', 'space-between', 'space-around']); randEnum(node, 0.5, 'alignItems', ['flex-start', 'center', 'flex-end', 'stretch']); randEnum(node, 0.5, 'alignSelf', ['flex-start', 'center', 'flex-end', 'stretch']); + randEnum(node, 0.5, 'alignContent', ['flex-start', 'center', 'flex-end', 'stretch']); randEnum(node, 0.5, 'position', ['relative', 'absolute']); randEnum(node, 0.5, 'flexWrap', ['nowrap', 'wrap']); //randEnum(node, 0.5, 'measure', [text(texts.small), text(texts.big)]); diff --git a/src/__tests__/Layout-test.c b/src/__tests__/Layout-test.c index 917ed223..02ed06e9 100644 --- a/src/__tests__/Layout-test.c +++ b/src/__tests__/Layout-test.c @@ -3965,7 +3965,7 @@ int main() css_node_t *node_0 = root_layout; node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; - node_0->layout.dimensions[CSS_WIDTH] = 34.671875; + node_0->layout.dimensions[CSS_WIDTH] = 35; node_0->layout.dimensions[CSS_HEIGHT] = 18; } @@ -4006,7 +4006,7 @@ int main() css_node_t *node_0 = root_layout; node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; - node_0->layout.dimensions[CSS_WIDTH] = 172.421875; + node_0->layout.dimensions[CSS_WIDTH] = 172; node_0->layout.dimensions[CSS_HEIGHT] = 18; } @@ -4192,7 +4192,7 @@ int main() node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; node_0->layout.dimensions[CSS_WIDTH] = 130; - node_0->layout.dimensions[CSS_HEIGHT] = 36; + node_0->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_0, 1); { css_node_t *node_1; @@ -4200,7 +4200,7 @@ int main() node_1->layout.position[CSS_TOP] = 0; node_1->layout.position[CSS_LEFT] = 0; node_1->layout.dimensions[CSS_WIDTH] = 130; - node_1->layout.dimensions[CSS_HEIGHT] = 36; + node_1->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_1, 1); { css_node_t *node_2; @@ -4208,7 +4208,7 @@ int main() node_2->layout.position[CSS_TOP] = 0; node_2->layout.position[CSS_LEFT] = 0; node_2->layout.dimensions[CSS_WIDTH] = 130; - node_2->layout.dimensions[CSS_HEIGHT] = 36; + node_2->layout.dimensions[CSS_HEIGHT] = 37; } } } @@ -4244,7 +4244,7 @@ int main() node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; node_0->layout.dimensions[CSS_WIDTH] = 200; - node_0->layout.dimensions[CSS_HEIGHT] = 36; + node_0->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_0, 1); { css_node_t *node_1; @@ -4252,7 +4252,7 @@ int main() node_1->layout.position[CSS_TOP] = 0; node_1->layout.position[CSS_LEFT] = 0; node_1->layout.dimensions[CSS_WIDTH] = 200; - node_1->layout.dimensions[CSS_HEIGHT] = 36; + node_1->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_1, 1); { css_node_t *node_2; @@ -4260,7 +4260,7 @@ int main() node_2->layout.position[CSS_TOP] = 0; node_2->layout.position[CSS_LEFT] = 0; node_2->layout.dimensions[CSS_WIDTH] = 130; - node_2->layout.dimensions[CSS_HEIGHT] = 36; + node_2->layout.dimensions[CSS_HEIGHT] = 37; } } } @@ -4290,15 +4290,15 @@ int main() node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; node_0->layout.dimensions[CSS_WIDTH] = 100; - node_0->layout.dimensions[CSS_HEIGHT] = 36; + node_0->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_0, 1); { css_node_t *node_1; node_1 = node_0->get_child(node_0->context, 0); node_1->layout.position[CSS_TOP] = 0; node_1->layout.position[CSS_LEFT] = 0; - node_1->layout.dimensions[CSS_WIDTH] = 100.4375; - node_1->layout.dimensions[CSS_HEIGHT] = 36; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 37; } } @@ -4344,23 +4344,23 @@ int main() node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; node_0->layout.dimensions[CSS_WIDTH] = 100; - node_0->layout.dimensions[CSS_HEIGHT] = 76; + node_0->layout.dimensions[CSS_HEIGHT] = 77; init_css_node_children(node_0, 1); { css_node_t *node_1; node_1 = node_0->get_child(node_0->context, 0); node_1->layout.position[CSS_TOP] = 20; node_1->layout.position[CSS_LEFT] = 20; - node_1->layout.dimensions[CSS_WIDTH] = 100.4375; - node_1->layout.dimensions[CSS_HEIGHT] = 36; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 37; init_css_node_children(node_1, 1); { css_node_t *node_2; node_2 = node_1->get_child(node_1->context, 0); node_2->layout.position[CSS_TOP] = 0; node_2->layout.position[CSS_LEFT] = 0; - node_2->layout.dimensions[CSS_WIDTH] = 100.4375; - node_2->layout.dimensions[CSS_HEIGHT] = 36; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 37; } } } @@ -4571,7 +4571,7 @@ int main() node_2 = node_1->get_child(node_1->context, 0); node_2->layout.position[CSS_TOP] = 20; node_2->layout.position[CSS_LEFT] = 20; - node_2->layout.dimensions[CSS_WIDTH] = 172.421875; + node_2->layout.dimensions[CSS_WIDTH] = 172; node_2->layout.dimensions[CSS_HEIGHT] = 18; } } @@ -4627,8 +4627,8 @@ int main() css_node_t *node_2; node_2 = node_1->get_child(node_1->context, 0); node_2->layout.position[CSS_TOP] = 20; - node_2->layout.position[CSS_LEFT] = 7.578125; - node_2->layout.dimensions[CSS_WIDTH] = 172.421875; + node_2->layout.position[CSS_LEFT] = 8; + node_2->layout.dimensions[CSS_WIDTH] = 172; node_2->layout.dimensions[CSS_HEIGHT] = 18; } } @@ -4668,7 +4668,7 @@ int main() node_0->layout.position[CSS_TOP] = 0; node_0->layout.position[CSS_LEFT] = 0; node_0->layout.dimensions[CSS_WIDTH] = 200; - node_0->layout.dimensions[CSS_HEIGHT] = 76; + node_0->layout.dimensions[CSS_HEIGHT] = 77; init_css_node_children(node_0, 1); { css_node_t *node_1; @@ -4676,7 +4676,7 @@ int main() node_1->layout.position[CSS_TOP] = 0; node_1->layout.position[CSS_LEFT] = 0; node_1->layout.dimensions[CSS_WIDTH] = 200; - node_1->layout.dimensions[CSS_HEIGHT] = 76; + node_1->layout.dimensions[CSS_HEIGHT] = 77; init_css_node_children(node_1, 1); { css_node_t *node_2; @@ -4684,7 +4684,7 @@ int main() node_2->layout.position[CSS_TOP] = 20; node_2->layout.position[CSS_LEFT] = 20; node_2->layout.dimensions[CSS_WIDTH] = 160; - node_2->layout.dimensions[CSS_HEIGHT] = 36; + node_2->layout.dimensions[CSS_HEIGHT] = 37; } } } @@ -7467,6 +7467,251 @@ int main() test("should layout node with correct start/end border in rtl", root_node, root_layout); } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.align_content = CSS_ALIGN_STRETCH; + node_0->style.align_items = CSS_ALIGN_FLEX_START; + node_0->style.flex_wrap = CSS_WRAP; + node_0->style.dimensions[CSS_WIDTH] = 300; + node_0->style.dimensions[CSS_HEIGHT] = 380; + init_css_node_children(node_0, 15); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 3); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 4); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 5); + node_1->style.align_self = CSS_ALIGN_FLEX_START; + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 6); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 7); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 8); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 9); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 10); + node_1->style.align_self = CSS_ALIGN_FLEX_START; + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 11); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 12); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 13); + node_1->style.align_self = CSS_ALIGN_FLEX_START; + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + node_1 = node_0->get_child(node_0->context, 14); + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 50; + node_1->style.margin[CSS_LEFT] = 10; + node_1->style.margin[CSS_TOP] = 10; + node_1->style.margin[CSS_RIGHT] = 10; + node_1->style.margin[CSS_BOTTOM] = 10; + node_1->style.margin[CSS_START] = 10; + node_1->style.margin[CSS_END] = 10; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 300; + node_0->layout.dimensions[CSS_HEIGHT] = 380; + init_css_node_children(node_0, 15); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 10; + node_1->layout.position[CSS_LEFT] = 10; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 10; + node_1->layout.position[CSS_LEFT] = 80; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 10; + node_1->layout.position[CSS_LEFT] = 150; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 3); + node_1->layout.position[CSS_TOP] = 10; + node_1->layout.position[CSS_LEFT] = 220; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 4); + node_1->layout.position[CSS_TOP] = 92.5; + node_1->layout.position[CSS_LEFT] = 10; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 5); + node_1->layout.position[CSS_TOP] = 92.5; + node_1->layout.position[CSS_LEFT] = 80; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 6); + node_1->layout.position[CSS_TOP] = 92.5; + node_1->layout.position[CSS_LEFT] = 150; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 7); + node_1->layout.position[CSS_TOP] = 92.5; + node_1->layout.position[CSS_LEFT] = 220; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 8); + node_1->layout.position[CSS_TOP] = 225; + node_1->layout.position[CSS_LEFT] = 10; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 9); + node_1->layout.position[CSS_TOP] = 225; + node_1->layout.position[CSS_LEFT] = 80; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 10); + node_1->layout.position[CSS_TOP] = 225; + node_1->layout.position[CSS_LEFT] = 150; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 11); + node_1->layout.position[CSS_TOP] = 225; + node_1->layout.position[CSS_LEFT] = 220; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 12); + node_1->layout.position[CSS_TOP] = 307.5; + node_1->layout.position[CSS_LEFT] = 10; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 13); + node_1->layout.position[CSS_TOP] = 307.5; + node_1->layout.position[CSS_LEFT] = 80; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + node_1 = node_0->get_child(node_0->context, 14); + node_1->layout.position[CSS_TOP] = 307.5; + node_1->layout.position[CSS_LEFT] = 150; + node_1->layout.dimensions[CSS_WIDTH] = 50; + node_1->layout.dimensions[CSS_HEIGHT] = 50; + } + } + + test("should layout with alignContent: stretch, and alignItems: flex-start", root_node, root_layout); + } /** END_GENERATED **/ return tests_finished(); } diff --git a/src/__tests__/Layout-test.js b/src/__tests__/Layout-test.js index d219acf3..3eddd369 100755 --- a/src/__tests__/Layout-test.js +++ b/src/__tests__/Layout-test.js @@ -9,6 +9,7 @@ /* globals layoutTestUtils */ var testLayout = layoutTestUtils.testLayout; +var testLayoutAgainstDomOnly = layoutTestUtils.testLayoutAgainstDomOnly; var testFillNodes = layoutTestUtils.testFillNodes; var testExtractNodes = layoutTestUtils.testExtractNodes; var text = layoutTestUtils.text; @@ -1389,7 +1390,7 @@ describe('Layout', function() { ]}, {width: 200, height: textSizes.smallHeight + 40, top: 0, left: 0, children: [ {width: 200, height: textSizes.smallHeight + 40, top: 0, left: 0, children: [ - {width: textSizes.bigWidth, height: textSizes.smallHeight, top: 20, left: 7.578125} + {width: textSizes.bigWidth, height: textSizes.smallHeight, top: 20, left: 8} ]} ]} ); @@ -2207,6 +2208,7 @@ describe('Layout', function() { ); }); + it('should give start/end padding precedence over left/right padding', function() { testLayout( {style: {width: 200, paddingLeft: 5, paddingStart: 15, paddingRight: 5, paddingEnd: 15}, children: [ @@ -2306,3 +2308,92 @@ describe('Layout', function() { ); }); }); + +describe('Layout alignContent', function() { + + it('should layout with alignContent: stretch, and alignItems: flex-start', function() { + testLayout( + {style: {width: 300, height: 380, flexDirection: 'row', flexWrap: 'wrap', alignContent: 'stretch', alignItems: 'flex-start'}, + children: [ + /* 0 */ {style: {width: 50, height: 50, margin: 10}}, + /* 1 */ {style: {width: 50, height: 50, margin: 10}}, + /* 2 */ {style: {width: 50, height: 50, margin: 10}}, + /* 3 */ {style: {width: 50, height: 50, margin: 10}}, + /* 4 */ {style: {width: 50, height: 100, margin: 10}}, + /* 5 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start'}}, + /* 6 */ {style: {width: 50, height: 50, margin: 10}}, + /* 7 */ {style: {width: 50, height: 100, margin: 10}}, + /* 8 */ {style: {width: 50, height: 50, margin: 10}}, + /* 9 */ {style: {width: 50, height: 50, margin: 10}}, + /* 10 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start' }}, + /* 11 */ {style: {width: 50, height: 50, margin: 10}}, + /* 12 */ {style: {width: 50, height: 50, margin: 10}}, + /* 13 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start'}}, + /* 14 */ {style: {width: 50, height: 50, margin: 10}}, + ], + }, + {width: 300, height: 380, top: 0, left: 0, children: [ + {width: 50, height: 50, top: 10, left: 10}, + {width: 50, height: 50, top: 10, left: 80}, + {width: 50, height: 50, top: 10, left: 150}, + {width: 50, height: 50, top: 10, left: 220}, + {width: 50, height: 100, top: 92.5, left: 10}, + {width: 50, height: 50, top: 92.5, left: 80}, + {width: 50, height: 50, top: 92.5, left: 150}, + {width: 50, height: 100, top: 92.5, left: 220}, + {width: 50, height: 50, top: 225, left: 10}, + {width: 50, height: 50, top: 225, left: 80}, + {width: 50, height: 50, top: 225, left: 150}, + {width: 50, height: 50, top: 225, left: 220}, + {width: 50, height: 50, top: 307.5, left: 10}, + {width: 50, height: 50, top: 307.5, left: 80}, + {width: 50, height: 50, top: 307.5, left: 150} + ]} + ); + }); + + + function testAlignContent(alignContent, alignItems) { + it('should layout with alignContent: ' + alignContent + ', and alignItems: ' + alignItems, function() { + testLayoutAgainstDomOnly( + {style: {width: 300, height: 380, flexDirection: 'row', flexWrap: 'wrap', alignContent: alignContent, alignItems: alignItems}, + children: [ + /* 0 */ {style: {width: 50, height: 50, margin: 10}}, + /* 1 */ {style: {width: 50, height: 50, margin: 10}}, + /* 2 */ {style: {width: 50, height: 50, margin: 10}}, + /* 3 */ {style: {width: 50, height: 50, margin: 10}}, + /* 4 */ {style: {width: 50, height: 100, margin: 10}}, + /* 5 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start'}}, + /* 6 */ {style: {width: 50, height: 50, margin: 10}}, + /* 7 */ {style: {width: 50, height: 100, margin: 10}}, + /* 8 */ {style: {width: 50, height: 50, margin: 10}}, + /* 9 */ {style: {width: 50, height: 50, margin: 10}}, + /* 10 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start' }}, + /* 11 */ {style: {width: 50, height: 50, margin: 10}}, + /* 12 */ {style: {width: 50, height: 50, margin: 10}}, + /* 13 */ {style: {width: 50, height: 50, margin: 10, alignSelf: 'flex-start'}}, + /* 14 */ {style: {width: 50, height: 50, margin: 10}}, + ], + } + ); + }); + } + testAlignContent('stretch', 'center'); + testAlignContent('stretch', 'flex-end'); + testAlignContent('stretch', 'stretch'); + + testAlignContent('flex-start', 'flex-start'); + testAlignContent('flex-start', 'center'); + testAlignContent('flex-start', 'flex-end'); + testAlignContent('flex-start', 'stretch'); + + testAlignContent('center', 'flex-start'); + testAlignContent('center', 'center'); + testAlignContent('center', 'flex-end'); + testAlignContent('center', 'stretch'); + + testAlignContent('flex-end', 'flex-start'); + testAlignContent('flex-end', 'center'); + testAlignContent('flex-end', 'flex-end'); + testAlignContent('flex-end', 'stretch'); +}); diff --git a/src/java/src/com/facebook/csslayout/CSSNode.java b/src/java/src/com/facebook/csslayout/CSSNode.java index 4135d734..36461152 100644 --- a/src/java/src/com/facebook/csslayout/CSSNode.java +++ b/src/java/src/com/facebook/csslayout/CSSNode.java @@ -54,6 +54,8 @@ public class CSSNode { /*package*/ final CSSLayout layout = new CSSLayout(); /*package*/ final CachedCSSLayout lastLayout = new CachedCSSLayout(); + public int lineIndex = 0; + // 4 is kinda arbitrary, but the default of 10 seems really high for an average View. private final ArrayList mChildren = new ArrayList(4); diff --git a/src/java/src/com/facebook/csslayout/CSSStyle.java b/src/java/src/com/facebook/csslayout/CSSStyle.java index 6d01db3d..15b10a5b 100644 --- a/src/java/src/com/facebook/csslayout/CSSStyle.java +++ b/src/java/src/com/facebook/csslayout/CSSStyle.java @@ -16,6 +16,7 @@ public class CSSStyle { public CSSDirection direction = CSSDirection.INHERIT; public CSSFlexDirection flexDirection = CSSFlexDirection.COLUMN; public CSSJustify justifyContent = CSSJustify.FLEX_START; + public CSSAlign alignContent = CSSAlign.FLEX_START; public CSSAlign alignItems = CSSAlign.STRETCH; public CSSAlign alignSelf = CSSAlign.AUTO; public CSSPositionType positionType = CSSPositionType.RELATIVE; diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index c1093c3e..41de2563 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -449,6 +449,10 @@ public class LayoutEngine { return node.style.alignItems; } + private static CSSAlign getAlignContent(CSSNode node) { + return node.style.alignContent; + } + private static CSSJustify getJustifyContent(CSSNode node) { return node.style.justifyContent; } @@ -626,6 +630,7 @@ public class LayoutEngine { // We aggregate the total dimensions of the container in those two variables float linesCrossDim = 0; float linesMainDim = 0; + int linesCount = 0; while (endLine < node.getChildCount()) { // Layout non flexible children and count children by type @@ -811,6 +816,7 @@ public class LayoutEngine { for (i = startLine; i < endLine; ++i) { child = node.getChildAt(i); + child.lineIndex = linesCount; if (getPositionType(child) == CSSPositionType.ABSOLUTE && isPosDefined(child, getLeading(mainAxis))) { @@ -856,7 +862,6 @@ public class LayoutEngine { } // Position elements in the cross axis - for (i = startLine; i < endLine; ++i) { child = node.getChildAt(i); @@ -915,9 +920,92 @@ public class LayoutEngine { linesCrossDim = linesCrossDim + crossDim; linesMainDim = Math.max(linesMainDim, mainDim); + linesCount = linesCount + 1; startLine = endLine; } + // + // + // Note(prenaux): More than one line, we need to layout the crossAxis + // according to alignContent. + // + // Note that we could probably remove and handle the one line case + // here too, but for the moment this is safer since it won't interfere with + // previously working code. + // + // See specs: + // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm + // section 9.4 + // + if (linesCount > 1 && + !CSSConstants.isUndefined(getLayoutDimension(node, getDim(crossAxis)))) { + float nodeCrossAxisInnerSize = getLayoutDimension(node, getDim(crossAxis)) - + getPaddingAndBorderAxis(node, crossAxis); + float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + + float crossDimLead = 0; + float currentLead = getLeadingPaddingAndBorder(node, crossAxis); + + CSSAlign alignContent = getAlignContent(node); + if (alignContent == CSSAlign.FLEX_END) { + currentLead = currentLead + remainingAlignContentDim; + } else if (alignContent == CSSAlign.CENTER) { + currentLead = currentLead + remainingAlignContentDim / 2; + } else if (alignContent == CSSAlign.STRETCH) { + if (nodeCrossAxisInnerSize > linesCrossDim) { + crossDimLead = (remainingAlignContentDim / linesCount); + } + } + + int endIndex = 0; + for (i = 0; i < linesCount; ++i) { + int startIndex = endIndex; + + // compute the line's height and find the endIndex + float lineHeight = 0; + for (ii = startIndex; ii < node.getChildCount(); ++ii) { + child = node.getChildAt(ii); + if (getPositionType(child) != CSSPositionType.RELATIVE) { + continue; + } + if (child.lineIndex != i) { + break; + } + if (!CSSConstants.isUndefined(getLayoutDimension(child, getDim(crossAxis)))) { + lineHeight = Math.max( + lineHeight, + getLayoutDimension(child, getDim(crossAxis)) + getMarginAxis(child, crossAxis) + ); + } + } + endIndex = ii; + lineHeight = lineHeight + crossDimLead; + + for (ii = startIndex; ii < endIndex; ++ii) { + child = node.getChildAt(ii); + if (getPositionType(child) != CSSPositionType.RELATIVE) { + continue; + } + + CSSAlign alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSSAlign.FLEX_START) { + setLayoutPosition(child, getPos(crossAxis), currentLead + getLeadingMargin(child, crossAxis)); + } else if (alignContentAlignItem == CSSAlign.FLEX_END) { + setLayoutPosition(child, getPos(crossAxis), currentLead + lineHeight - getTrailingMargin(child, crossAxis) - getLayoutDimension(child, getDim(crossAxis))); + } else if (alignContentAlignItem == CSSAlign.CENTER) { + float childHeight = getLayoutDimension(child, getDim(crossAxis)); + setLayoutPosition(child, getPos(crossAxis), currentLead + (lineHeight - childHeight) / 2); + } else if (alignContentAlignItem == CSSAlign.STRETCH) { + setLayoutPosition(child, getPos(crossAxis), currentLead + getLeadingMargin(child, crossAxis)); + // TODO(prenaux): Correctly set the height of items with undefined + // (auto) crossAxis dimension. + } + } + + currentLead = currentLead + lineHeight; + } + } + boolean needsMainTrailingPos = false; boolean needsCrossTrailingPos = false; @@ -947,8 +1035,7 @@ public class LayoutEngine { needsCrossTrailingPos = true; } - // Set trailing position if necessary - + // Set trailing position if necessary if (needsMainTrailingPos || needsCrossTrailingPos) { for (i = 0; i < node.getChildCount(); ++i) { child = node.getChildAt(i); @@ -963,8 +1050,7 @@ public class LayoutEngine { } } - // Calculate dimensions for absolutely positioned elements - + // Calculate dimensions for absolutely positioned elements for (i = 0; i < node.getChildCount(); ++i) { child = node.getChildAt(i); if (getPositionType(child) == CSSPositionType.ABSOLUTE) { diff --git a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java index 9104b514..0526655c 100644 --- a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java +++ b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java @@ -4230,7 +4230,7 @@ public class LayoutEngineTest { TestCSSNode node_0 = root_layout; node_0.layout.top = 0; node_0.layout.left = 0; - node_0.layout.width = 34.671875f; + node_0.layout.width = 35; node_0.layout.height = 18; } @@ -4275,7 +4275,7 @@ public class LayoutEngineTest { TestCSSNode node_0 = root_layout; node_0.layout.top = 0; node_0.layout.left = 0; - node_0.layout.width = 172.421875f; + node_0.layout.width = 172; node_0.layout.height = 18; } @@ -4469,7 +4469,7 @@ public class LayoutEngineTest { node_0.layout.top = 0; node_0.layout.left = 0; node_0.layout.width = 130; - node_0.layout.height = 36; + node_0.layout.height = 37; addChildren(node_0, 1); { TestCSSNode node_1; @@ -4477,7 +4477,7 @@ public class LayoutEngineTest { node_1.layout.top = 0; node_1.layout.left = 0; node_1.layout.width = 130; - node_1.layout.height = 36; + node_1.layout.height = 37; addChildren(node_1, 1); { TestCSSNode node_2; @@ -4485,7 +4485,7 @@ public class LayoutEngineTest { node_2.layout.top = 0; node_2.layout.left = 0; node_2.layout.width = 130; - node_2.layout.height = 36; + node_2.layout.height = 37; } } } @@ -4523,7 +4523,7 @@ public class LayoutEngineTest { node_0.layout.top = 0; node_0.layout.left = 0; node_0.layout.width = 200; - node_0.layout.height = 36; + node_0.layout.height = 37; addChildren(node_0, 1); { TestCSSNode node_1; @@ -4531,7 +4531,7 @@ public class LayoutEngineTest { node_1.layout.top = 0; node_1.layout.left = 0; node_1.layout.width = 200; - node_1.layout.height = 36; + node_1.layout.height = 37; addChildren(node_1, 1); { TestCSSNode node_2; @@ -4539,7 +4539,7 @@ public class LayoutEngineTest { node_2.layout.top = 0; node_2.layout.left = 0; node_2.layout.width = 130; - node_2.layout.height = 36; + node_2.layout.height = 37; } } } @@ -4571,15 +4571,15 @@ public class LayoutEngineTest { node_0.layout.top = 0; node_0.layout.left = 0; node_0.layout.width = 100; - node_0.layout.height = 36; + node_0.layout.height = 37; addChildren(node_0, 1); { TestCSSNode node_1; node_1 = node_0.getChildAt(0); node_1.layout.top = 0; node_1.layout.left = 0; - node_1.layout.width = 100.4375f; - node_1.layout.height = 36; + node_1.layout.width = 100; + node_1.layout.height = 37; } } @@ -4627,23 +4627,23 @@ public class LayoutEngineTest { node_0.layout.top = 0; node_0.layout.left = 0; node_0.layout.width = 100; - node_0.layout.height = 76; + node_0.layout.height = 77; addChildren(node_0, 1); { TestCSSNode node_1; node_1 = node_0.getChildAt(0); node_1.layout.top = 20; node_1.layout.left = 20; - node_1.layout.width = 100.4375f; - node_1.layout.height = 36; + node_1.layout.width = 100; + node_1.layout.height = 37; addChildren(node_1, 1); { TestCSSNode node_2; node_2 = node_1.getChildAt(0); node_2.layout.top = 0; node_2.layout.left = 0; - node_2.layout.width = 100.4375f; - node_2.layout.height = 36; + node_2.layout.width = 100; + node_2.layout.height = 37; } } } @@ -4864,7 +4864,7 @@ public class LayoutEngineTest { node_2 = node_1.getChildAt(0); node_2.layout.top = 20; node_2.layout.left = 20; - node_2.layout.width = 172.421875f; + node_2.layout.width = 172; node_2.layout.height = 18; } } @@ -4922,8 +4922,8 @@ public class LayoutEngineTest { TestCSSNode node_2; node_2 = node_1.getChildAt(0); node_2.layout.top = 20; - node_2.layout.left = 7.578125f; - node_2.layout.width = 172.421875f; + node_2.layout.left = 8; + node_2.layout.width = 172; node_2.layout.height = 18; } } @@ -4965,7 +4965,7 @@ public class LayoutEngineTest { node_0.layout.top = 0; node_0.layout.left = 0; node_0.layout.width = 200; - node_0.layout.height = 76; + node_0.layout.height = 77; addChildren(node_0, 1); { TestCSSNode node_1; @@ -4973,7 +4973,7 @@ public class LayoutEngineTest { node_1.layout.top = 0; node_1.layout.left = 0; node_1.layout.width = 200; - node_1.layout.height = 76; + node_1.layout.height = 77; addChildren(node_1, 1); { TestCSSNode node_2; @@ -4981,7 +4981,7 @@ public class LayoutEngineTest { node_2.layout.top = 20; node_2.layout.left = 20; node_2.layout.width = 160; - node_2.layout.height = 36; + node_2.layout.height = 37; } } } @@ -7896,5 +7896,252 @@ public class LayoutEngineTest { test("should layout node with correct start/end border in rtl", root_node, root_layout); } + + @Test + public void testCase177() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.alignContent = CSSAlign.STRETCH; + node_0.style.alignItems = CSSAlign.FLEX_START; + node_0.style.flexWrap = CSSWrap.WRAP; + node_0.style.width = 300; + node_0.style.height = 380; + addChildren(node_0, 15); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(1); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(2); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(3); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(4); + node_1.style.width = 50; + node_1.style.height = 100; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(5); + node_1.style.alignSelf = CSSAlign.FLEX_START; + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(6); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(7); + node_1.style.width = 50; + node_1.style.height = 100; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(8); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(9); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(10); + node_1.style.alignSelf = CSSAlign.FLEX_START; + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(11); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(12); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(13); + node_1.style.alignSelf = CSSAlign.FLEX_START; + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + node_1 = node_0.getChildAt(14); + node_1.style.width = 50; + node_1.style.height = 50; + node_1.setMargin(Spacing.LEFT, 10); + node_1.setMargin(Spacing.TOP, 10); + node_1.setMargin(Spacing.RIGHT, 10); + node_1.setMargin(Spacing.BOTTOM, 10); + node_1.setMargin(Spacing.START, 10); + node_1.setMargin(Spacing.END, 10); + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.top = 0; + node_0.layout.left = 0; + node_0.layout.width = 300; + node_0.layout.height = 380; + addChildren(node_0, 15); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.top = 10; + node_1.layout.left = 10; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(1); + node_1.layout.top = 10; + node_1.layout.left = 80; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(2); + node_1.layout.top = 10; + node_1.layout.left = 150; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(3); + node_1.layout.top = 10; + node_1.layout.left = 220; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(4); + node_1.layout.top = 92.5f; + node_1.layout.left = 10; + node_1.layout.width = 50; + node_1.layout.height = 100; + node_1 = node_0.getChildAt(5); + node_1.layout.top = 92.5f; + node_1.layout.left = 80; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(6); + node_1.layout.top = 92.5f; + node_1.layout.left = 150; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(7); + node_1.layout.top = 92.5f; + node_1.layout.left = 220; + node_1.layout.width = 50; + node_1.layout.height = 100; + node_1 = node_0.getChildAt(8); + node_1.layout.top = 225; + node_1.layout.left = 10; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(9); + node_1.layout.top = 225; + node_1.layout.left = 80; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(10); + node_1.layout.top = 225; + node_1.layout.left = 150; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(11); + node_1.layout.top = 225; + node_1.layout.left = 220; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(12); + node_1.layout.top = 307.5f; + node_1.layout.left = 10; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(13); + node_1.layout.top = 307.5f; + node_1.layout.left = 80; + node_1.layout.width = 50; + node_1.layout.height = 50; + node_1 = node_0.getChildAt(14); + node_1.layout.top = 307.5f; + node_1.layout.left = 150; + node_1.layout.width = 50; + node_1.layout.height = 50; + } + } + + test("should layout with alignContent: stretch, and alignItems: flex-start", root_node, root_layout); + } /** END_GENERATED **/ } diff --git a/src/java/tests/com/facebook/csslayout/TestConstants.java b/src/java/tests/com/facebook/csslayout/TestConstants.java index 3332e1e6..6ca0d1b3 100644 --- a/src/java/tests/com/facebook/csslayout/TestConstants.java +++ b/src/java/tests/com/facebook/csslayout/TestConstants.java @@ -14,11 +14,11 @@ package com.facebook.csslayout; public class TestConstants { /** START_GENERATED **/ - public static final float SMALL_WIDTH = 34.671875f; + public static final float SMALL_WIDTH = 35f; public static final float SMALL_HEIGHT = 18f; - public static final float BIG_WIDTH = 172.421875f; - public static final float BIG_HEIGHT = 36f; - public static final float BIG_MIN_WIDTH = 100.4375f; + public static final float BIG_WIDTH = 172f; + public static final float BIG_HEIGHT = 37f; + public static final float BIG_MIN_WIDTH = 100f; public static final String SMALL_TEXT = "small"; public static final String LONG_TEXT = "loooooooooong with space"; /** END_GENERATED **/ diff --git a/src/transpile.js b/src/transpile.js index e2f7b268..7ce1bae2 100644 --- a/src/transpile.js +++ b/src/transpile.js @@ -20,6 +20,8 @@ global.layoutTestUtils = { testLayout: function(node, expectedLayout) { allTests.push({name: currentTest, node: node, expectedLayout: expectedLayout}); }, + testLayoutAgainstDomOnly: function(node) { + }, testRandomLayout: function(node, i) { allTests.push({name: 'Random #' + i, node: node, expectedLayout: computeDOMLayout(node)}); }, @@ -31,7 +33,8 @@ global.layoutTestUtils = { }; global.describe = function(name, cb) { - if (name === 'Layout') { + if (name === 'Layout' || + name === 'Layout alignContent') { cb(); } }; @@ -132,6 +135,12 @@ function printLayout(test) { 'space-between': 'CSS_JUSTIFY_SPACE_BETWEEN', 'space-around': 'CSS_JUSTIFY_SPACE_AROUND' }); + addEnum(node, 'alignContent', 'align_content', { + 'flex-start': 'CSS_ALIGN_FLEX_START', + 'center': 'CSS_ALIGN_CENTER', + 'flex-end': 'CSS_ALIGN_FLEX_END', + 'stretch': 'CSS_ALIGN_STRETCH' + }); addEnum(node, 'alignItems', 'align_items', { 'flex-start': 'CSS_ALIGN_FLEX_START', 'center': 'CSS_ALIGN_CENTER', @@ -241,12 +250,14 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/\.maxHeight/g, '.maxDimensions[CSS_HEIGHT]') .replace(/\.minWidth/g, '.minDimensions[CSS_WIDTH]') .replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]') + .replace(/\.lineIndex/g, '.line_index') .replace(/layout\[dim/g, 'layout.dimensions[dim') .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') .replace(/layout\[trailing/g, 'layout.position[trailing') .replace(/style\[dim/g, 'style.dimensions[dim') .replace(/node.children\[i\]/g, 'node->get_child(node->context, i)') + .replace(/node.children\[ii\]/g, 'node->get_child(node->context, ii)') .replace(/node\./g, 'node->') .replace(/child\./g, 'child->') .replace(/parent\./g, 'parent->')