diff --git a/Gruntfile.js b/Gruntfile.js index 3e7cfbd8..9fca1a20 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,6 +6,7 @@ module.exports = function(grunt) { var isWindows = /^win/.test(process.platform); require('load-grunt-tasks')(grunt); + grunt.loadNpmTasks('grunt-contrib-concat'); // config var config = { @@ -102,6 +103,35 @@ module.exports = function(grunt) { } }, + concat: { + options: { + separator: '\n', + // Replace all 'use strict' statements in the code with a single one at the top + banner: [ + '/*', + ' * #define CSS_LAYOUT_IMPLEMENTATION', + ' * before you include this file in *one* C or C++ file to create the implementation.', + ' */\n' + ].join('\n'), + process: function(src, filepath) { + if (path.extname(filepath) === '.c') { + return [ + '#ifdef CSS_LAYOUT_IMPLEMENTATION', + src, + '#endif // CSS_LAYOUT_IMPLEMENTATION' + ].join('\n') + } + else { + return src; + } + }, + }, + dist: { + src: ['<%= config.srcFolder %>/Layout.h', '<%= config.srcFolder %>/Layout.c'], + dest: '<%= config.distFolder %>/css-layout.h', + }, + }, + shell: { cCompile: { command: cTestCompile @@ -148,12 +178,15 @@ module.exports = function(grunt) { // Packages the Java as a JAR grunt.registerTask('package-java', ['shell:javaPackage']); + // Packages the C code as a single header + grunt.registerTask('package-c', ['concat']); + // Default build, performs the full works! - grunt.registerTask('build', ['test-javascript', 'transpile', 'clean:dist', 'package-javascript', 'package-java']); + grunt.registerTask('build', ['test-javascript', 'transpile', 'clean:dist', 'package-javascript', 'package-java', 'package-c']); // The JavaScript unit tests require Chrome (they need a faithful flexbox implementation // to test against), so under CI this step is skipped. - grunt.registerTask('ci', ['eslint', 'transpile', 'clean:dist', 'package-javascript', 'package-java']); + grunt.registerTask('ci', ['eslint', 'transpile', 'clean:dist', 'package-javascript', 'package-java', 'package-c']); grunt.registerTask('default', ['build']); }; diff --git a/dist/css-layout.h b/dist/css-layout.h new file mode 100644 index 00000000..47ae3f3f --- /dev/null +++ b/dist/css-layout.h @@ -0,0 +1,1315 @@ +/* + * #define CSS_LAYOUT_IMPLEMENTATION + * before you include this file in *one* C or C++ file to create the implementation. + */ +/** + * 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. + */ + +#ifndef __LAYOUT_H +#define __LAYOUT_H + +#include +#ifndef __cplusplus +#include +#endif + +// Not defined in MSVC++ +#ifndef NAN +static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; +#define NAN (*(const float *)__nan) +#endif + +#define CSS_UNDEFINED NAN + +typedef enum { + CSS_DIRECTION_INHERIT = 0, + CSS_DIRECTION_LTR, + CSS_DIRECTION_RTL +} css_direction_t; + +typedef enum { + CSS_FLEX_DIRECTION_COLUMN = 0, + CSS_FLEX_DIRECTION_COLUMN_REVERSE, + CSS_FLEX_DIRECTION_ROW, + CSS_FLEX_DIRECTION_ROW_REVERSE +} css_flex_direction_t; + +typedef enum { + CSS_JUSTIFY_FLEX_START = 0, + CSS_JUSTIFY_CENTER, + CSS_JUSTIFY_FLEX_END, + CSS_JUSTIFY_SPACE_BETWEEN, + CSS_JUSTIFY_SPACE_AROUND +} css_justify_t; + +// Note: auto is only a valid value for alignSelf. It is NOT a valid value for +// alignItems. +typedef enum { + CSS_ALIGN_AUTO = 0, + CSS_ALIGN_FLEX_START, + CSS_ALIGN_CENTER, + CSS_ALIGN_FLEX_END, + CSS_ALIGN_STRETCH +} css_align_t; + +typedef enum { + CSS_POSITION_RELATIVE = 0, + CSS_POSITION_ABSOLUTE +} css_position_type_t; + +typedef enum { + CSS_NOWRAP = 0, + CSS_WRAP +} css_wrap_type_t; + +// Note: left and top are shared between position[2] and position[4], so +// they have to be before right and bottom. +typedef enum { + CSS_LEFT = 0, + CSS_TOP, + CSS_RIGHT, + CSS_BOTTOM, + CSS_START, + CSS_END, + CSS_POSITION_COUNT +} css_position_t; + +typedef enum { + CSS_WIDTH = 0, + CSS_HEIGHT +} css_dimension_t; + +typedef struct { + float position[4]; + float dimensions[2]; + css_direction_t direction; + + // 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_dimensions[2]; + float last_position[2]; + css_direction_t last_direction; +} css_layout_t; + +typedef struct { + float dimensions[2]; +} css_dim_t; + +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; + css_wrap_type_t flex_wrap; + float flex; + float margin[6]; + float position[4]; + /** + * You should skip all the rules that contain negative values for the + * following attributes. For example: + * {padding: 10, paddingLeft: -5} + * should output: + * {left: 10 ...} + * the following two are incorrect: + * {left: -5 ...} + * {left: 0 ...} + */ + float padding[6]; + float border[6]; + float dimensions[2]; + float minDimensions[2]; + float maxDimensions[2]; +} css_style_t; + +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); + struct css_node* (*get_child)(void *context, int i); + bool (*is_dirty)(void *context); + void *context; +} css_node_t; + + +// Lifecycle of nodes and children +css_node_t *new_css_node(void); +void init_css_node(css_node_t *node); +void free_css_node(css_node_t *node); + +// Print utilities +typedef enum { + CSS_PRINT_LAYOUT = 1, + CSS_PRINT_STYLE = 2, + CSS_PRINT_CHILDREN = 4, +} 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 maxWidth, css_direction_t parentDirection); +bool isUndefined(float value); + +#endif + +#ifdef CSS_LAYOUT_IMPLEMENTATION +/** + * 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. + */ + +#include +#include +#include +#include + +// in concatenated header, don't include Layout.h it's already at the top +#ifndef CSS_LAYOUT_IMPLEMENTATION +#include "Layout.h" +#endif + +#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); +} + +static bool eq(float a, float b) { + if (isUndefined(a)) { + return isUndefined(b); + } + return fabs(a - b) < 0.0001; +} + +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; + + // Some of the fields default to undefined and not 0 + node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + node->style.position[CSS_LEFT] = CSS_UNDEFINED; + node->style.position[CSS_TOP] = CSS_UNDEFINED; + node->style.position[CSS_RIGHT] = CSS_UNDEFINED; + node->style.position[CSS_BOTTOM] = CSS_UNDEFINED; + + node->style.margin[CSS_START] = CSS_UNDEFINED; + node->style.margin[CSS_END] = CSS_UNDEFINED; + node->style.padding[CSS_START] = CSS_UNDEFINED; + node->style.padding[CSS_END] = CSS_UNDEFINED; + node->style.border[CSS_START] = CSS_UNDEFINED; + node->style.border[CSS_END] = CSS_UNDEFINED; + + node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + + // Such that the comparison is always going to be false + 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 = (css_direction_t)-1; + node->layout.should_update = true; +} + +css_node_t *new_css_node() { + css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); + init_css_node(node); + return node; +} + +void free_css_node(css_node_t *node) { + free(node); +} + +static void indent(int n) { + for (int i = 0; i < n; ++i) { + printf(" "); + } +} + +static void print_number_0(const char *str, float number) { + if (!eq(number, 0)) { + printf("%s: %g, ", str, number); + } +} + +static void print_number_nan(const char *str, float number) { + if (!isnan(number)) { + printf("%s: %g, ", str, number); + } +} + +static bool four_equal(float four[4]) { + return + eq(four[0], four[1]) && + eq(four[0], four[2]) && + eq(four[0], four[3]); +} + + +static void print_css_node_rec( + css_node_t *node, + css_print_options_t options, + int level +) { + indent(level); + printf("{"); + + if (node->print) { + node->print(node->context); + } + + if (options & CSS_PRINT_LAYOUT) { + printf("layout: {"); + printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]); + printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]); + printf("top: %g, ", node->layout.position[CSS_TOP]); + printf("left: %g", node->layout.position[CSS_LEFT]); + printf("}, "); + } + + if (options & CSS_PRINT_STYLE) { + if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { + printf("flexDirection: 'column', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { + printf("flexDirection: 'columnReverse', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { + printf("flexDirection: 'row', "); + } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + printf("flexDirection: 'rowReverse', "); + } + + if (node->style.justify_content == CSS_JUSTIFY_CENTER) { + printf("justifyContent: 'center', "); + } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) { + printf("justifyContent: 'flex-end', "); + } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) { + printf("justifyContent: 'space-around', "); + } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) { + printf("justifyContent: 'space-between', "); + } + + if (node->style.align_items == CSS_ALIGN_CENTER) { + printf("alignItems: 'center', "); + } else if (node->style.align_items == CSS_ALIGN_FLEX_END) { + printf("alignItems: 'flex-end', "); + } else if (node->style.align_items == CSS_ALIGN_STRETCH) { + 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) { + printf("alignSelf: 'center', "); + } else if (node->style.align_self == CSS_ALIGN_FLEX_END) { + printf("alignSelf: 'flex-end', "); + } else if (node->style.align_self == CSS_ALIGN_STRETCH) { + printf("alignSelf: 'stretch', "); + } + + print_number_nan("flex", node->style.flex); + + if (four_equal(node->style.margin)) { + print_number_0("margin", node->style.margin[CSS_LEFT]); + } else { + print_number_0("marginLeft", node->style.margin[CSS_LEFT]); + print_number_0("marginRight", node->style.margin[CSS_RIGHT]); + print_number_0("marginTop", node->style.margin[CSS_TOP]); + print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]); + print_number_0("marginStart", node->style.margin[CSS_START]); + print_number_0("marginEnd", node->style.margin[CSS_END]); + } + + if (four_equal(node->style.padding)) { + print_number_0("padding", node->style.margin[CSS_LEFT]); + } else { + print_number_0("paddingLeft", node->style.padding[CSS_LEFT]); + print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); + print_number_0("paddingTop", node->style.padding[CSS_TOP]); + print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]); + print_number_0("paddingStart", node->style.padding[CSS_START]); + print_number_0("paddingEnd", node->style.padding[CSS_END]); + } + + if (four_equal(node->style.border)) { + print_number_0("borderWidth", node->style.border[CSS_LEFT]); + } else { + print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]); + print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]); + print_number_0("borderTopWidth", node->style.border[CSS_TOP]); + print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]); + print_number_0("borderStartWidth", node->style.border[CSS_START]); + print_number_0("borderEndWidth", node->style.border[CSS_END]); + } + + print_number_nan("width", node->style.dimensions[CSS_WIDTH]); + print_number_nan("height", node->style.dimensions[CSS_HEIGHT]); + + if (node->style.position_type == CSS_POSITION_ABSOLUTE) { + printf("position: 'absolute', "); + } + + print_number_nan("left", node->style.position[CSS_LEFT]); + print_number_nan("right", node->style.position[CSS_RIGHT]); + print_number_nan("top", node->style.position[CSS_TOP]); + print_number_nan("bottom", node->style.position[CSS_BOTTOM]); + } + + if (options & CSS_PRINT_CHILDREN && node->children_count > 0) { + printf("children: [\n"); + for (int i = 0; i < node->children_count; ++i) { + print_css_node_rec(node->get_child(node->context, i), options, level + 1); + } + indent(level); + printf("]},\n"); + } else { + printf("},\n"); + } +} + +void print_css_node(css_node_t *node, css_print_options_t options) { + print_css_node_rec(node, options, 0); +} + + +static css_position_t leading[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT +}; +static css_position_t trailing[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT +}; +static css_position_t pos[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT +}; +static css_dimension_t dim[4] = { + /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT, + /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT, + /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH, + /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH +}; + +static bool isRowDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_ROW || + flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE; +} + +static bool isColumnDirection(css_flex_direction_t flex_direction) { + return flex_direction == CSS_FLEX_DIRECTION_COLUMN || + flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; +} + +static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { + return node->style.margin[CSS_START]; + } + + return node->style.margin[leading[axis]]; +} + +static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { + return node->style.margin[CSS_END]; + } + + return node->style.margin[trailing[axis]]; +} + +static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_START]) && + node->style.padding[CSS_START] >= 0) { + return node->style.padding[CSS_START]; + } + + if (node->style.padding[leading[axis]] >= 0) { + return node->style.padding[leading[axis]]; + } + + return 0; +} + +static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.padding[CSS_END]) && + node->style.padding[CSS_END] >= 0) { + return node->style.padding[CSS_END]; + } + + if (node->style.padding[trailing[axis]] >= 0) { + return node->style.padding[trailing[axis]]; + } + + return 0; +} + +static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_START]) && + node->style.border[CSS_START] >= 0) { + return node->style.border[CSS_START]; + } + + if (node->style.border[leading[axis]] >= 0) { + return node->style.border[leading[axis]]; + } + + return 0; +} + +static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { + if (isRowDirection(axis) && + !isUndefined(node->style.border[CSS_END]) && + node->style.border[CSS_END] >= 0) { + return node->style.border[CSS_END]; + } + + if (node->style.border[trailing[axis]] >= 0) { + return node->style.border[trailing[axis]]; + } + + return 0; +} + +static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); +} + +static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { + return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); +} + +static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); +} + +static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); +} + +static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { + return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); +} + +static css_position_type_t getPositionType(css_node_t *node) { + return node->style.position_type; +} + +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; + } + return node->style.align_items; +} + +static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { + css_direction_t direction = node->style.direction; + + if (direction == CSS_DIRECTION_INHERIT) { + direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR; + } + + return direction; +} + +static css_flex_direction_t getFlexDirection(css_node_t *node) { + return node->style.flex_direction; +} + +static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) { + if (direction == CSS_DIRECTION_RTL) { + if (flex_direction == CSS_FLEX_DIRECTION_ROW) { + return CSS_FLEX_DIRECTION_ROW_REVERSE; + } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { + return CSS_FLEX_DIRECTION_ROW; + } + } + + return flex_direction; +} + +static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) { + if (isColumnDirection(flex_direction)) { + return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + } else { + return CSS_FLEX_DIRECTION_COLUMN; + } +} + +static float getFlex(css_node_t *node) { + return node->style.flex; +} + +static bool isFlex(css_node_t *node) { + return ( + getPositionType(node) == CSS_POSITION_RELATIVE && + getFlex(node) > 0 + ); +} + +static bool isFlexWrap(css_node_t *node) { + return node->style.flex_wrap == CSS_WRAP; +} + +static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { + return node->layout.dimensions[dim[axis]] + + getLeadingMargin(node, axis) + + getTrailingMargin(node, axis); +} + +static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) { + float value = node->style.dimensions[dim[axis]]; + return !isUndefined(value) && value > 0.0; +} + +static bool isPosDefined(css_node_t *node, css_position_t position) { + return !isUndefined(node->style.position[position]); +} + +static bool isMeasureDefined(css_node_t *node) { + return node->measure; +} + +static float getPosition(css_node_t *node, css_position_t position) { + float result = node->style.position[position]; + if (!isUndefined(result)) { + return result; + } + return 0; +} + +static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { + float min = CSS_UNDEFINED; + float max = CSS_UNDEFINED; + + if (isColumnDirection(axis)) { + min = node->style.minDimensions[CSS_HEIGHT]; + max = node->style.maxDimensions[CSS_HEIGHT]; + } else if (isRowDirection(axis)) { + min = node->style.minDimensions[CSS_WIDTH]; + max = node->style.maxDimensions[CSS_WIDTH]; + } + + float boundValue = value; + + if (!isUndefined(max) && max >= 0.0 && boundValue > max) { + boundValue = max; + } + if (!isUndefined(min) && min >= 0.0 && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + +// When the user specifically sets a value for width or height +static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { + // The parent already computed us a width or height. We just skip it + if (!isUndefined(node->layout.dimensions[dim[axis]])) { + return; + } + // We only run if there's a width or height defined + if (!isDimDefined(node, axis)) { + return; + } + + // The dimensions can never be smaller than the padding and border + node->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(node, axis, node->style.dimensions[dim[axis]]), + getPaddingAndBorderAxis(node, axis) + ); +} + +static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { + child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - + child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; + } + +// If both left and right are defined, then use left. Otherwise return +// +left or -right depending on which is defined. +static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { + float lead = node->style.position[leading[axis]]; + if (!isUndefined(lead)) { + return lead; + } + return -getPosition(node, trailing[axis]); +} + +static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { + /** START_GENERATED **/ + css_direction_t direction = resolveDirection(node, parentDirection); + css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); + css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); + css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + + // Handle width and height style attributes + setDimensionFromStyle(node, mainAxis); + setDimensionFromStyle(node, crossAxis); + + // Set the resolved resolution in the node's layout + node->layout.direction = direction; + + // The position is set by the parent, but we need to complete it with a + // delta composed of the margin and left/top/right/bottom + node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + + if (isMeasureDefined(node)) { + float width = CSS_UNDEFINED; + if (isDimDefined(node, resolvedRowAxis)) { + width = node->style.dimensions[CSS_WIDTH]; + } else if (!isUndefined(node->layout.dimensions[dim[resolvedRowAxis]])) { + width = node->layout.dimensions[dim[resolvedRowAxis]]; + } else { + width = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis); + } + width -= getPaddingAndBorderAxis(node, resolvedRowAxis); + + // We only need to give a dimension for the text if we haven't got any + // for it computed yet. It can either be from the style attribute or because + // the element is flexible. + bool isRowUndefined = !isDimDefined(node, resolvedRowAxis) && + isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]); + bool isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && + isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); + + // Let's not measure the text if we already know both dimensions + if (isRowUndefined || isColumnUndefined) { + css_dim_t measureDim = node->measure( + node->context, + + width + ); + if (isRowUndefined) { + node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + + getPaddingAndBorderAxis(node, resolvedRowAxis); + } + if (isColumnUndefined) { + node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + + getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + } + } + if (node->children_count == 0) { + return; + } + } + + int i; + int ii; + css_node_t* child; + css_flex_direction_t axis; + + // Pre-fill some dimensions straight from the parent + for (i = 0; i < node->children_count; ++i) { + child = node->get_child(node->context, i); + // Pre-fill cross axis dimensions when the child is using stretch before + // we call the recursive layout pass + if (getAlignItem(node, child) == CSS_ALIGN_STRETCH && + getPositionType(child) == CSS_POSITION_RELATIVE && + !isUndefined(node->layout.dimensions[dim[crossAxis]]) && + !isDimDefined(child, crossAxis)) { + child->layout.dimensions[dim[crossAxis]] = fmaxf( + boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - + getPaddingAndBorderAxis(node, crossAxis) - + getMarginAxis(child, crossAxis)), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, crossAxis) + ); + } else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) { + // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both + // left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + if (!isUndefined(node->layout.dimensions[dim[axis]]) && + !isDimDefined(child, axis) && + isPosDefined(child, leading[axis]) && + isPosDefined(child, trailing[axis])) { + child->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getPaddingAndBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis])), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, axis) + ); + } + } + } + } + + float definedMainDim = CSS_UNDEFINED; + if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { + definedMainDim = node->layout.dimensions[dim[mainAxis]] - + getPaddingAndBorderAxis(node, mainAxis); + } + + // We want to execute the next two loops one per line with flex-wrap + int startLine = 0; + int endLine = 0; + // int nextOffset = 0; + int alreadyComputedNextLayout = 0; + // We aggregate the total dimensions of the container in those two variables + float linesCrossDim = 0; + float linesMainDim = 0; + int linesCount = 0; + while (endLine < node->children_count) { + // Layout non flexible children and count children by type + + // mainContentDim is accumulation of the dimensions and margin of all the + // non flexible children. This will be used in order to either set the + // dimensions of the node if none already exist, or to compute the + // remaining space left for the flexible children. + float mainContentDim = 0; + + // There are three kind of children, non flexible, flexible and absolute. + // We need to know how many there are in order to distribute the space. + int flexibleChildrenCount = 0; + float totalFlexible = 0; + int nonFlexibleChildrenCount = 0; + + float maxWidth; + for (i = startLine; i < node->children_count; ++i) { + child = node->get_child(node->context, i); + float nextContentDim = 0; + + // It only makes sense to consider a child flexible if we have a computed + // dimension for the node-> + if (!isUndefined(node->layout.dimensions[dim[mainAxis]]) && isFlex(child)) { + flexibleChildrenCount++; + totalFlexible += getFlex(child); + + // Even if we don't know its exact size yet, we already know the padding, + // border and margin. We'll use this partial information, which represents + // the smallest possible size for the child, to compute the remaining + // available space. + nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + + getMarginAxis(child, mainAxis); + + } else { + maxWidth = CSS_UNDEFINED; + if (!isRowDirection(mainAxis)) { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + getPaddingAndBorderAxis(node, resolvedRowAxis); + + if (isDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + getPaddingAndBorderAxis(node, resolvedRowAxis); + } + } + + // This is the main recursive call. We layout non flexible children. + if (alreadyComputedNextLayout == 0) { + layoutNode(child, maxWidth, direction); + } + + // Absolute positioned elements do not take part of the layout, so we + // don't use them to compute mainContentDim + if (getPositionType(child) == CSS_POSITION_RELATIVE) { + nonFlexibleChildrenCount++; + // At this point we know the final size and margin of the element. + nextContentDim = getDimWithMargin(child, mainAxis); + } + } + + // The element we are about to add would make us go to the next line + if (isFlexWrap(node) && + !isUndefined(node->layout.dimensions[dim[mainAxis]]) && + mainContentDim + nextContentDim > definedMainDim && + // If there's only one element, then it's bigger than the content + // and needs its own line + i != startLine) { + nonFlexibleChildrenCount--; + alreadyComputedNextLayout = 1; + break; + } + alreadyComputedNextLayout = 0; + mainContentDim += nextContentDim; + endLine = i + 1; + } + + // Layout flexible children and allocate empty space + + // In order to position the elements in the main axis, we have two + // controls. The space between the beginning and the first element + // and the space between each two elements. + float leadingMainDim = 0; + float betweenMainDim = 0; + + // The remaining available space that needs to be allocated + float remainingMainDim = 0; + if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { + remainingMainDim = definedMainDim - mainContentDim; + } else { + remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + } + + // If there are flexible children in the mix, they are going to fill the + // remaining space + if (flexibleChildrenCount != 0) { + float flexibleMainDim = remainingMainDim / totalFlexible; + float baseMainDim; + float boundMainDim; + + // Iterate over every child in the axis. If the flex share of remaining + // space doesn't meet min/max bounds, remove this child from flex + // calculations. + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); + if (isFlex(child)) { + baseMainDim = flexibleMainDim * getFlex(child) + + getPaddingAndBorderAxis(child, mainAxis); + boundMainDim = boundAxis(child, mainAxis, baseMainDim); + + if (baseMainDim != boundMainDim) { + remainingMainDim -= boundMainDim; + totalFlexible -= getFlex(child); + } + } + } + flexibleMainDim = remainingMainDim / totalFlexible; + + // The non flexible children can overflow the container, in this case + // we should just assume that there is no space available. + if (flexibleMainDim < 0) { + flexibleMainDim = 0; + } + // We iterate over the full array and only apply the action on flexible + // children. This is faster than actually allocating a new array that + // contains only flexible children. + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); + if (isFlex(child)) { + // At this point we know the final size of the element in the main + // dimension + child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis, + flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis) + ); + + maxWidth = CSS_UNDEFINED; + if (isDimDefined(node, resolvedRowAxis)) { + maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - + getPaddingAndBorderAxis(node, resolvedRowAxis); + } else if (!isRowDirection(mainAxis)) { + maxWidth = parentMaxWidth - + getMarginAxis(node, resolvedRowAxis) - + getPaddingAndBorderAxis(node, resolvedRowAxis); + } + + // And we recursively call the layout algorithm for this child + layoutNode(child, maxWidth, direction); + } + } + + // We use justifyContent to figure out how to allocate the remaining + // space available + } else { + css_justify_t justifyContent = getJustifyContent(node); + if (justifyContent == CSS_JUSTIFY_CENTER) { + leadingMainDim = remainingMainDim / 2; + } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { + leadingMainDim = remainingMainDim; + } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) { + remainingMainDim = fmaxf(remainingMainDim, 0); + if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { + betweenMainDim = remainingMainDim / + (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + } else { + betweenMainDim = 0; + } + } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) { + // Space on the edges is half of the space between elements + betweenMainDim = remainingMainDim / + (flexibleChildrenCount + nonFlexibleChildrenCount); + leadingMainDim = betweenMainDim / 2; + } + } + + // Position elements in the main axis and compute dimensions + + // At this point, all the children have their dimensions set. We need to + // find their position. In order to do that, we accumulate data in + // variables that are also useful to compute the total dimensions of the + // container! + float crossDim = 0; + float mainDim = leadingMainDim + + getLeadingPaddingAndBorder(node, mainAxis); + + 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])) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); + } else { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child->layout.position[pos[mainAxis]] += mainDim; + + // Define the trailing position accordingly. + if (!isUndefined(node->layout.dimensions[dim[mainAxis]])) { + setTrailingPosition(node, child, mainAxis); + } + } + + // Now that we placed the element, we need to update the variables + // We only need to do that for relative elements. Absolute elements + // do not take part in that phase. + if (getPositionType(child) == CSS_POSITION_RELATIVE) { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + } + } + + float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; + if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { + containerCrossAxis = fmaxf( + // For the cross dim, we add both sides at the end because the value + // is aggregate via a max function. Intermediate negative values + // can mess this computation otherwise + boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)), + getPaddingAndBorderAxis(node, crossAxis) + ); + } + + // Position elements in the cross axis + for (i = startLine; i < endLine; ++i) { + child = node->get_child(node->context, i); + + if (getPositionType(child) == CSS_POSITION_ABSOLUTE && + isPosDefined(child, leading[crossAxis])) { + // In case the child is absolutely positionned and has a + // top/left/bottom/right being set, we override all the previously + // computed positions to set it correctly. + child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + + } else { + float leadingCrossDim = getLeadingPaddingAndBorder(node, crossAxis); + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis + if (getPositionType(child) == CSS_POSITION_RELATIVE) { + css_align_t alignItem = getAlignItem(node, child); + if (alignItem == CSS_ALIGN_STRETCH) { + // You can only stretch if the dimension has not already been set + // previously. + if (!isDimDefined(child, crossAxis)) { + child->layout.dimensions[dim[crossAxis]] = fmaxf( + boundAxis(child, crossAxis, containerCrossAxis - + getPaddingAndBorderAxis(node, crossAxis) - + getMarginAxis(child, crossAxis)), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, crossAxis) + ); + } + } else if (alignItem != CSS_ALIGN_FLEX_START) { + // The remaining space between the parent dimensions+padding and child + // dimensions+margin. + float remainingCrossDim = containerCrossAxis - + getPaddingAndBorderAxis(node, crossAxis) - + getDimWithMargin(child, crossAxis); + + if (alignItem == CSS_ALIGN_CENTER) { + leadingCrossDim += remainingCrossDim / 2; + } else { // CSS_ALIGN_FLEX_END + leadingCrossDim += remainingCrossDim; + } + } + } + + // And we apply the position + child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; + + // Define the trailing position accordingly. + if (!isUndefined(node->layout.dimensions[dim[crossAxis]])) { + setTrailingPosition(node, child, crossAxis); + } + } + } + + 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; + + // If the user didn't specify a width or height, and it has not been set + // by the container, then we set it via the children. + if (isUndefined(node->layout.dimensions[dim[mainAxis]])) { + node->layout.dimensions[dim[mainAxis]] = fmaxf( + // We're missing the last padding at this point to get the final + // dimension + boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), + // We can never assign a width smaller than the padding and borders + getPaddingAndBorderAxis(node, mainAxis) + ); + + needsMainTrailingPos = true; + } + + if (isUndefined(node->layout.dimensions[dim[crossAxis]])) { + node->layout.dimensions[dim[crossAxis]] = fmaxf( + // For the cross dim, we add both sides at the end because the value + // is aggregate via a max function. Intermediate negative values + // can mess this computation otherwise + boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)), + getPaddingAndBorderAxis(node, crossAxis) + ); + + needsCrossTrailingPos = true; + } + + // Set trailing position if necessary + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < node->children_count; ++i) { + child = node->get_child(node->context, i); + + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } + } + } + + // 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) { + // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both + // left and right or top and bottom). + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + if (!isUndefined(node->layout.dimensions[dim[axis]]) && + !isDimDefined(child, axis) && + isPosDefined(child, leading[axis]) && + isPosDefined(child, trailing[axis])) { + child->layout.dimensions[dim[axis]] = fmaxf( + boundAxis(child, axis, node->layout.dimensions[dim[axis]] - + getBorderAxis(node, axis) - + getMarginAxis(child, axis) - + getPosition(child, leading[axis]) - + getPosition(child, trailing[axis]) + ), + // You never want to go smaller than padding + getPaddingAndBorderAxis(child, axis) + ); + } + } + for (ii = 0; ii < 2; ii++) { + axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + if (isPosDefined(child, trailing[axis]) && + !isPosDefined(child, leading[axis])) { + child->layout.position[leading[axis]] = + node->layout.dimensions[dim[axis]] - + child->layout.dimensions[dim[axis]] - + getPosition(child, trailing[axis]); + } + } + } + } + /** END_GENERATED **/ +} + +void layoutNode(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) { + css_layout_t *layout = &node->layout; + css_direction_t direction = node->style.direction; + layout->should_update = true; + + bool skipLayout = + !node->is_dirty(node->context) && + eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && + eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && + eq(layout->last_parent_max_width, parentMaxWidth); + eq(layout->last_direction, direction); + + if (skipLayout) { + layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; + layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT]; + layout->position[CSS_TOP] = layout->last_position[CSS_TOP]; + layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT]; + } else { + layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; + layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; + layout->last_parent_max_width = parentMaxWidth; + layout->last_direction = direction; + + layoutNodeImpl(node, parentMaxWidth, parentDirection); + + layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; + layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; + layout->last_position[CSS_TOP] = layout->position[CSS_TOP]; + layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; + } +} + +#endif // CSS_LAYOUT_IMPLEMENTATION \ No newline at end of file diff --git a/package.json b/package.json index 812f0f29..30ada27c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "grunt": "^0.4.5", "grunt-cli": "^0.1.13", "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-concat": "^0.5.1", "grunt-contrib-copy": "^0.8.0", "grunt-contrib-uglify": "^0.9.1", "grunt-eslint": "^17.1.0", diff --git a/src/Layout.c b/src/Layout.c index e9c24e27..c4da1d7e 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -12,7 +12,10 @@ #include #include +// in concatenated header, don't include Layout.h it's already at the top +#ifndef CSS_LAYOUT_IMPLEMENTATION #include "Layout.h" +#endif #ifdef _MSC_VER #include