| |
| /** |
| * 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 <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| // in concatenated header, don't include Layout.h it's already at the top |
| #ifndef CSS_LAYOUT_IMPLEMENTATION |
| #include "WXLayoutDefine.h" |
| #endif |
| |
| #ifdef _MSC_VER |
| #include <float.h> |
| #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_parent_max_height = -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_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 ( |
| node->style.position_type == 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 isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) { |
| float value = node->style.dimensions[dim[axis]]; |
| return !isUndefined(value) && value >= 0.0; |
| } |
| |
| static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) { |
| float value = node->layout.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 (isLayoutDimDefined(node, axis)) { |
| return; |
| } |
| // We only run if there's a width or height defined |
| if (!isStyleDimDefined(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, float parentMaxHeight, 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); |
| |
| // Inline immutable values from the target node to avoid excessive method |
| // invocations during the layout calculation. |
| int childCount = node->children_count; |
| float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); |
| float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); |
| |
| if (isMeasureDefined(node)) { |
| bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); |
| |
| float width = CSS_UNDEFINED; |
| css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED; |
| if (isStyleDimDefined(node, resolvedRowAxis)) { |
| width = node->style.dimensions[CSS_WIDTH]; |
| widthMode = CSS_MEASURE_MODE_EXACTLY; |
| } else if (isResolvedRowDimDefined) { |
| width = node->layout.dimensions[dim[resolvedRowAxis]]; |
| widthMode = CSS_MEASURE_MODE_EXACTLY; |
| } else { |
| width = parentMaxWidth - |
| getMarginAxis(node, resolvedRowAxis); |
| widthMode = CSS_MEASURE_MODE_AT_MOST; |
| } |
| width -= paddingAndBorderAxisResolvedRow; |
| if (isUndefined(width)) { |
| widthMode = CSS_MEASURE_MODE_UNDEFINED; |
| } |
| |
| float height = CSS_UNDEFINED; |
| css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED; |
| if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { |
| height = node->style.dimensions[CSS_HEIGHT]; |
| heightMode = CSS_MEASURE_MODE_EXACTLY; |
| } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { |
| height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; |
| heightMode = CSS_MEASURE_MODE_EXACTLY; |
| } else { |
| height = parentMaxHeight - |
| getMarginAxis(node, resolvedRowAxis); |
| heightMode = CSS_MEASURE_MODE_AT_MOST; |
| } |
| height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); |
| if (isUndefined(height)) { |
| heightMode = CSS_MEASURE_MODE_UNDEFINED; |
| } |
| |
| // 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 = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; |
| bool isColumnUndefined = !isStyleDimDefined(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, |
| widthMode, |
| height, |
| heightMode |
| ); |
| if (isRowUndefined) { |
| node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + |
| paddingAndBorderAxisResolvedRow; |
| } |
| if (isColumnUndefined) { |
| node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + |
| paddingAndBorderAxisColumn; |
| } |
| } |
| if (childCount == 0) { |
| return; |
| } |
| } |
| |
| bool isNodeFlexWrap = isFlexWrap(node); |
| |
| css_justify_t justifyContent = node->style.justify_content; |
| |
| float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); |
| float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); |
| float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); |
| float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); |
| |
| bool isMainDimDefined = isLayoutDimDefined(node, mainAxis); |
| bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis); |
| bool isMainRowDirection = isRowDirection(mainAxis); |
| |
| int i; |
| int ii; |
| css_node_t* child; |
| css_flex_direction_t axis; |
| |
| css_node_t* firstAbsoluteChild = NULL; |
| css_node_t* currentAbsoluteChild = NULL; |
| |
| float definedMainDim = CSS_UNDEFINED; |
| if (isMainDimDefined) { |
| definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; |
| } |
| |
| // 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 < childCount) { |
| // <Loop A> 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; |
| |
| // Use the line loop to position children in the main axis for as long |
| // as they are using a simple stacking behaviour. Children that are |
| // immediately stacked in the initial loop will not be touched again |
| // in <Loop C>. |
| bool isSimpleStackMain = |
| (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) || |
| (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER); |
| int firstComplexMain = (isSimpleStackMain ? childCount : startLine); |
| |
| // Use the initial line loop to position children in the cross axis for |
| // as long as they are relatively positioned with alignment STRETCH or |
| // FLEX_START. Children that are immediately stacked in the initial loop |
| // will not be touched again in <Loop D>. |
| bool isSimpleStackCross = true; |
| int firstComplexCross = childCount; |
| |
| css_node_t* firstFlexChild = NULL; |
| css_node_t* currentFlexChild = NULL; |
| |
| float mainDim = leadingPaddingAndBorderMain; |
| float crossDim = 0; |
| |
| float maxWidth = CSS_UNDEFINED; |
| float maxHeight = CSS_UNDEFINED; |
| for (i = startLine; i < childCount; ++i) { |
| child = node->get_child(node->context, i); |
| if (child == NULL) { |
| return; |
| } |
| child->line_index = linesCount; |
| |
| child->next_absolute_child = NULL; |
| child->next_flex_child = NULL; |
| |
| css_align_t alignItem = getAlignItem(node, child); |
| |
| // Pre-fill cross axis dimensions when the child is using stretch before |
| // we call the recursive layout pass |
| if (alignItem == CSS_ALIGN_STRETCH && |
| child->style.position_type == CSS_POSITION_RELATIVE && |
| isCrossDimDefined && |
| !isStyleDimDefined(child, crossAxis)) { |
| child->layout.dimensions[dim[crossAxis]] = fmaxf( |
| boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - |
| paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), |
| // You never want to go smaller than padding |
| getPaddingAndBorderAxis(child, crossAxis) |
| ); |
| } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) { |
| // Store a private linked list of absolutely positioned children |
| // so that we can efficiently traverse them later. |
| if (firstAbsoluteChild == NULL) { |
| firstAbsoluteChild = child; |
| } |
| if (currentAbsoluteChild != NULL) { |
| currentAbsoluteChild->next_absolute_child = child; |
| } |
| currentAbsoluteChild = child; |
| |
| // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both |
| // left and right or top and bottom). |
| for (ii = 0; ii < 2; ii++) { |
| axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; |
| if (isLayoutDimDefined(node, axis) && |
| !isStyleDimDefined(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 nextContentDim = 0; |
| |
| // It only makes sense to consider a child flexible if we have a computed |
| // dimension for the node-> |
| if (isMainDimDefined && isFlex(child)) { |
| flexibleChildrenCount++; |
| totalFlexible += child->style.flex; |
| |
| // Store a private linked list of flexible children so that we can |
| // efficiently traverse them later. |
| if (firstFlexChild == NULL) { |
| firstFlexChild = child; |
| } |
| if (currentFlexChild != NULL) { |
| currentFlexChild->next_flex_child = child; |
| } |
| currentFlexChild = 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; |
| maxHeight = CSS_UNDEFINED; |
| |
| if (!isMainRowDirection) { |
| if (isLayoutDimDefined(node, resolvedRowAxis)) { |
| maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - |
| paddingAndBorderAxisResolvedRow; |
| } else { |
| maxWidth = parentMaxWidth - |
| getMarginAxis(node, resolvedRowAxis) - |
| paddingAndBorderAxisResolvedRow; |
| } |
| } else { |
| if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { |
| maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - |
| paddingAndBorderAxisColumn; |
| } else { |
| maxHeight = parentMaxHeight - |
| getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - |
| paddingAndBorderAxisColumn; |
| } |
| } |
| |
| // This is the main recursive call. We layout non flexible children. |
| if (alreadyComputedNextLayout == 0) { |
| layoutNode(child, maxWidth, maxHeight, direction); |
| } |
| |
| // Absolute positioned elements do not take part of the layout, so we |
| // don't use them to compute mainContentDim |
| if (child->style.position_type == 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 (isNodeFlexWrap && |
| isMainDimDefined && |
| 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; |
| } |
| |
| // Disable simple stacking in the main axis for the current line as |
| // we found a non-trivial child-> The remaining children will be laid out |
| // in <Loop C>. |
| if (isSimpleStackMain && |
| (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) { |
| isSimpleStackMain = false; |
| firstComplexMain = i; |
| } |
| |
| // Disable simple stacking in the cross axis for the current line as |
| // we found a non-trivial child-> The remaining children will be laid out |
| // in <Loop D>. |
| if (isSimpleStackCross && |
| (child->style.position_type != CSS_POSITION_RELATIVE || |
| (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) || |
| (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { |
| isSimpleStackCross = false; |
| firstComplexCross = i; |
| } |
| |
| if (isSimpleStackMain) { |
| child->layout.position[pos[mainAxis]] += mainDim; |
| if (isMainDimDefined) { |
| setTrailingPosition(node, child, mainAxis); |
| } |
| |
| mainDim += getDimWithMargin(child, mainAxis); |
| crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); |
| } |
| |
| if (isSimpleStackCross) { |
| child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; |
| if (isCrossDimDefined) { |
| setTrailingPosition(node, child, crossAxis); |
| } |
| } |
| |
| alreadyComputedNextLayout = 0; |
| mainContentDim += nextContentDim; |
| endLine = i + 1; |
| } |
| |
| // <Loop B> 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 (isMainDimDefined) { |
| 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; |
| |
| // If the flex share of remaining space doesn't meet min/max bounds, |
| // remove this child from flex calculations. |
| currentFlexChild = firstFlexChild; |
| while (currentFlexChild != NULL) { |
| baseMainDim = flexibleMainDim * currentFlexChild->style.flex + |
| getPaddingAndBorderAxis(currentFlexChild, mainAxis); |
| boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); |
| |
| if (baseMainDim != boundMainDim) { |
| remainingMainDim -= boundMainDim; |
| totalFlexible -= currentFlexChild->style.flex; |
| } |
| |
| currentFlexChild = currentFlexChild->next_flex_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; |
| } |
| |
| currentFlexChild = firstFlexChild; |
| while (currentFlexChild != NULL) { |
| // At this point we know the final size of the element in the main |
| // dimension |
| currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, |
| flexibleMainDim * currentFlexChild->style.flex + |
| getPaddingAndBorderAxis(currentFlexChild, mainAxis) |
| ); |
| |
| maxWidth = CSS_UNDEFINED; |
| if (isLayoutDimDefined(node, resolvedRowAxis)) { |
| maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - |
| paddingAndBorderAxisResolvedRow; |
| } else if (!isMainRowDirection) { |
| maxWidth = parentMaxWidth - |
| getMarginAxis(node, resolvedRowAxis) - |
| paddingAndBorderAxisResolvedRow; |
| } |
| maxHeight = CSS_UNDEFINED; |
| if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { |
| maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - |
| paddingAndBorderAxisColumn; |
| } else if (isMainRowDirection) { |
| maxHeight = parentMaxHeight - |
| getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - |
| paddingAndBorderAxisColumn; |
| } |
| |
| // And we recursively call the layout algorithm for this child |
| layoutNode(currentFlexChild, maxWidth, maxHeight, direction); |
| |
| child = currentFlexChild; |
| currentFlexChild = currentFlexChild->next_flex_child; |
| child->next_flex_child = NULL; |
| } |
| |
| // We use justifyContent to figure out how to allocate the remaining |
| // space available |
| } else if (justifyContent != CSS_JUSTIFY_FLEX_START) { |
| 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; |
| } |
| } |
| |
| // <Loop C> 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! |
| mainDim += leadingMainDim; |
| |
| for (i = firstComplexMain; i < endLine; ++i) { |
| child = node->get_child(node->context, i); |
| |
| if (child->style.position_type == 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 (isMainDimDefined) { |
| 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 (child->style.position_type == 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 (!isCrossDimDefined) { |
| 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 + paddingAndBorderAxisCross), |
| paddingAndBorderAxisCross |
| ); |
| } |
| |
| // <Loop D> Position elements in the cross axis |
| for (i = firstComplexCross; i < endLine; ++i) { |
| child = node->get_child(node->context, i); |
| |
| if (child->style.position_type == 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 = leadingPaddingAndBorderCross; |
| |
| // For a relative children, we're either using alignItems (parent) or |
| // alignSelf (child) in order to determine the position in the cross axis |
| if (child->style.position_type == CSS_POSITION_RELATIVE) { |
| /*eslint-disable */ |
| // This variable is intentionally re-defined as the code is transpiled to a block scope language |
| css_align_t alignItem = getAlignItem(node, child); |
| /*eslint-enable */ |
| if (alignItem == CSS_ALIGN_STRETCH) { |
| // You can only stretch if the dimension has not already been defined |
| // previously. |
| if (!isStyleDimDefined(child, crossAxis)) { |
| float dimCrossAxis = child->layout.dimensions[dim[crossAxis]]; |
| child->layout.dimensions[dim[crossAxis]] = fmaxf( |
| boundAxis(child, crossAxis, containerCrossAxis - |
| paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), |
| // You never want to go smaller than padding |
| getPaddingAndBorderAxis(child, crossAxis) |
| ); |
| |
| // If the size has changed, and this child has children we need to re-layout this child |
| if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) { |
| // Reset child margins before re-layout as they are added back in layoutNode and would be doubled |
| child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + |
| getRelativePosition(child, mainAxis); |
| child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + |
| getRelativePosition(child, mainAxis); |
| child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + |
| getRelativePosition(child, crossAxis); |
| child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + |
| getRelativePosition(child, crossAxis); |
| |
| layoutNode(child, maxWidth, maxHeight, direction); |
| } |
| } |
| } else if (alignItem != CSS_ALIGN_FLEX_START) { |
| // The remaining space between the parent dimensions+padding and child |
| // dimensions+margin. |
| float remainingCrossDim = containerCrossAxis - |
| paddingAndBorderAxisCross - 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 (isCrossDimDefined) { |
| setTrailingPosition(node, child, crossAxis); |
| } |
| } |
| } |
| |
| linesCrossDim += crossDim; |
| linesMainDim = fmaxf(linesMainDim, mainDim); |
| linesCount += 1; |
| startLine = endLine; |
| } |
| |
| // <Loop E> |
| // |
| // Note(prenaux): More than one line, we need to layout the crossAxis |
| // according to alignContent. |
| // |
| // Note that we could probably remove <Loop D> 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 && isCrossDimDefined) { |
| float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - |
| paddingAndBorderAxisCross; |
| float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; |
| |
| float crossDimLead = 0; |
| float currentLead = leadingPaddingAndBorderCross; |
| |
| css_align_t alignContent = node->style.align_content; |
| 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 < childCount; ++ii) { |
| child = node->get_child(node->context, ii); |
| if (child->style.position_type != CSS_POSITION_RELATIVE) { |
| continue; |
| } |
| if (child->line_index != i) { |
| break; |
| } |
| if (isLayoutDimDefined(child, 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 (child->style.position_type != 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 (!isMainDimDefined) { |
| 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 |
| paddingAndBorderAxisMain |
| ); |
| |
| if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || |
| mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { |
| needsMainTrailingPos = true; |
| } |
| } |
| |
| if (!isCrossDimDefined) { |
| 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 + paddingAndBorderAxisCross), |
| paddingAndBorderAxisCross |
| ); |
| |
| if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || |
| crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { |
| needsCrossTrailingPos = true; |
| } |
| } |
| |
| // <Loop F> Set trailing position if necessary |
| if (needsMainTrailingPos || needsCrossTrailingPos) { |
| for (i = 0; i < childCount; ++i) { |
| child = node->get_child(node->context, i); |
| |
| if (needsMainTrailingPos) { |
| setTrailingPosition(node, child, mainAxis); |
| } |
| |
| if (needsCrossTrailingPos) { |
| setTrailingPosition(node, child, crossAxis); |
| } |
| } |
| } |
| |
| // <Loop G> Calculate dimensions for absolutely positioned elements |
| currentAbsoluteChild = firstAbsoluteChild; |
| while (currentAbsoluteChild != NULL) { |
| // Pre-fill dimensions when using absolute position and both offsets for |
| // the axis are defined (either both left and right or top and bottom). |
| for (ii = 0; ii < 2; ii++) { |
| axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; |
| |
| if (isLayoutDimDefined(node, axis) && |
| !isStyleDimDefined(currentAbsoluteChild, axis) && |
| isPosDefined(currentAbsoluteChild, leading[axis]) && |
| isPosDefined(currentAbsoluteChild, trailing[axis])) { |
| currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf( |
| boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] - |
| getBorderAxis(node, axis) - |
| getMarginAxis(currentAbsoluteChild, axis) - |
| getPosition(currentAbsoluteChild, leading[axis]) - |
| getPosition(currentAbsoluteChild, trailing[axis]) |
| ), |
| // You never want to go smaller than padding |
| getPaddingAndBorderAxis(currentAbsoluteChild, axis) |
| ); |
| } |
| |
| if (isPosDefined(currentAbsoluteChild, trailing[axis]) && |
| !isPosDefined(currentAbsoluteChild, leading[axis])) { |
| currentAbsoluteChild->layout.position[leading[axis]] = |
| node->layout.dimensions[dim[axis]] - |
| currentAbsoluteChild->layout.dimensions[dim[axis]] - |
| getPosition(currentAbsoluteChild, trailing[axis]); |
| } |
| } |
| |
| child = currentAbsoluteChild; |
| currentAbsoluteChild = currentAbsoluteChild->next_absolute_child; |
| child->next_absolute_child = NULL; |
| } |
| /** END_GENERATED **/ |
| } |
| |
| void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, 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_parent_max_height, parentMaxHeight) && |
| 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_parent_max_height = parentMaxHeight; |
| layout->last_direction = direction; |
| |
| for (int i = 0, childCount = node->children_count; i < childCount; i++) { |
| resetNodeLayout(node->get_child(node->context, i)); |
| } |
| |
| layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, 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]; |
| } |
| } |
| |
| void resetNodeLayout(css_node_t *node) { |
| node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; |
| node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; |
| node->layout.position[CSS_LEFT] = 0; |
| node->layout.position[CSS_TOP] = 0; |
| } |