Exemple #1
0
        /**
         * This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
         */

        public void resetResult()
        {
            FillArray(position, 0);
            FillArray(dimensions, CSSConstants.Undefined);

            direction = CSSDirection.LTR;
        }
Exemple #2
0
 public void SetDirection(CSSDirection direction)
 {
     if (this.style.Direction != direction)
     {
         this.style.Direction = direction;
         Dirty();
     }
 }
Exemple #3
0
 public void Copy(CSSLayout layout)
 {
     position [POSITION_LEFT] = layout.position [POSITION_LEFT];
     position [POSITION_TOP] = layout.position [POSITION_TOP];
     position [POSITION_RIGHT] = layout.position [POSITION_RIGHT];
     position [POSITION_BOTTOM] = layout.position [POSITION_BOTTOM];
     dimensions [DIMENSION_WIDTH] = layout.dimensions [DIMENSION_WIDTH];
     dimensions [DIMENSION_HEIGHT] = layout.dimensions [DIMENSION_HEIGHT];
     direction = layout.direction;
 }
Exemple #4
0
 public void Copy(CSSLayout layout)
 {
     position [POSITION_LEFT]      = layout.position [POSITION_LEFT];
     position [POSITION_TOP]       = layout.position [POSITION_TOP];
     position [POSITION_RIGHT]     = layout.position [POSITION_RIGHT];
     position [POSITION_BOTTOM]    = layout.position [POSITION_BOTTOM];
     dimensions [DIMENSION_WIDTH]  = layout.dimensions [DIMENSION_WIDTH];
     dimensions [DIMENSION_HEIGHT] = layout.dimensions [DIMENSION_HEIGHT];
     direction = layout.direction;
 }
Exemple #5
0
        internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, CSSDirection? parentDirection)
        {
            if (needsRelayout(node, parentMaxWidth))
            {
                node.lastLayout.requestedWidth = node.layout.dimensions[DIMENSION_WIDTH];
                node.lastLayout.requestedHeight = node.layout.dimensions[DIMENSION_HEIGHT];
                node.lastLayout.parentMaxWidth = parentMaxWidth;

                layoutNodeImpl(layoutContext, node, parentMaxWidth, parentDirection);
                node.lastLayout.copy(node.layout);
            }
            else
            {
                node.layout.copy(node.lastLayout);
            }

            node.markHasNewLayout();
        }
Exemple #6
0
        public void resetResult()
        {
            FillArray(position, 0);
            FillArray(dimensions, CSSConstants.Undefined);

            direction = CSSDirection.LTR;

            flexBasis = 0;

            generationCount     = 0;
            lastParentDirection = null;

            nextCachedMeasurementsIndex          = 0;
            measuredDimensions[DIMENSION_WIDTH]  = CSSConstants.Undefined;
            measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.Undefined;

            cachedLayout.widthMeasureMode  = null;
            cachedLayout.heightMeasureMode = null;
        }
Exemple #7
0
        static CSSDirection resolveDirection(CSSNode node, CSSDirection? parentDirection)
        {
            CSSDirection direction = node.style.direction;
            if (direction == CSSDirection.Inherit)
            {
                direction = (parentDirection == null ? CSSDirection.LTR : parentDirection.Value);
            }

            return direction;
        }
Exemple #8
0
        static int resolveAxis(int axis, CSSDirection direction)
        {
            if (direction == CSSDirection.RTL)
            {
                if (axis == CSS_FLEX_DIRECTION_ROW)
                {
                    return CSS_FLEX_DIRECTION_ROW_REVERSE;
                }
                else if (axis == CSS_FLEX_DIRECTION_ROW_REVERSE)
                {
                    return CSS_FLEX_DIRECTION_ROW;
                }
            }

            return axis;
        }
        private static void setPosition(CSSNode node, CSSDirection direction)
        {
            int mainAxis = resolveAxis(getFlexDirection(node), direction);
            int crossAxis = getCrossFlexDirection(mainAxis, direction);

            node.layout.position[leading[mainAxis]] = node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
                getRelativePosition(node, mainAxis);
            node.layout.position[trailing[mainAxis]] = node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
                getRelativePosition(node, mainAxis);
            node.layout.position[leading[crossAxis]] = node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
                getRelativePosition(node, crossAxis);
            node.layout.position[trailing[crossAxis]] = node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
                getRelativePosition(node, crossAxis);
        }
Exemple #10
0
 public void ResetResult()
 {
     ArrayUtils.Fill (position, 0f);
     ArrayUtils.Fill(dimensions, CSSConstants.UNDEFINED);
     direction = CSSDirection.LTR;
 }
        internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection)
        {
            // Increment the generation count. This will force the recursive routine to visit
            // all dirty nodes at least once. Subsequent visits will be skipped if the input
            // parameters don't change.
            layoutContext.currentGenerationCount++;

            CSSMeasureMode widthMeasureMode = CSSMeasureMode.Undefined;
            CSSMeasureMode heightMeasureMode = CSSMeasureMode.Undefined;

            if (!float.IsNaN(availableWidth)) {
              widthMeasureMode = CSSMeasureMode.Exactly;
            } else if (node.style.dimensions[DIMENSION_WIDTH] >= 0.0) {
              float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
              availableWidth = node.style.dimensions[DIMENSION_WIDTH] + marginAxisRow;
              widthMeasureMode = CSSMeasureMode.Exactly;
            } else if (node.style.maxWidth >= 0.0) {
              availableWidth = node.style.maxWidth;
              widthMeasureMode = CSSMeasureMode.AtMost;
            }

            if (!float.IsNaN(availableHeight)) {
              heightMeasureMode = CSSMeasureMode.Exactly;
            } else if (node.style.dimensions[DIMENSION_HEIGHT] >= 0.0) {
              float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
              availableHeight = node.style.dimensions[DIMENSION_HEIGHT] + marginAxisColumn;
              heightMeasureMode = CSSMeasureMode.Exactly;
            } else if (node.style.maxHeight >= 0.0) {
              availableHeight = node.style.maxHeight;
              heightMeasureMode = CSSMeasureMode.AtMost;
            }

            if (layoutNodeInternal(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial"))
            {
                setPosition(node, node.layout.direction);
            }
        }
Exemple #12
0
 public static extern void CSSNodeCalculateLayout(IntPtr node,
                     float availableWidth,
                     float availableHeight,
                     CSSDirection parentDirection);
Exemple #13
0
 public static extern void CSSNodeStyleSetDirection(IntPtr node, CSSDirection direction);
Exemple #14
0
 private static int getCrossFlexDirection(int axis, CSSDirection direction)
 {
     if (axis == CSS_FLEX_DIRECTION_COLUMN || axis == CSS_FLEX_DIRECTION_COLUMN_REVERSE)
     {
         return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
     }
     else
     {
         return CSS_FLEX_DIRECTION_COLUMN;
     }
 }
Exemple #15
0
 public void ResetResult()
 {
     ArrayUtils.Fill(position, 0f);
     ArrayUtils.Fill(dimensions, CSSConstants.UNDEFINED);
     direction = CSSDirection.LTR;
 }
        //
        // This is a wrapper around the layoutNodeImpl function. It determines
        // whether the layout request is redundant and can be skipped.
        //
        // Parameters:
        //  Input parameters are the same as layoutNodeImpl (see below)
        //  Return parameter is true if layout was performed, false if skipped
        //
        internal static boolean layoutNodeInternal(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection, CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, boolean performLayout, string reason)
        {
            CSSLayout layout = node.layout;

            boolean needToVisitNode = (node.isDirty() && layout.generationCount != layoutContext.currentGenerationCount) ||
              layout.lastParentDirection != parentDirection;

            if (needToVisitNode)
            {
                // Invalidate the cached results.
                layout.nextCachedMeasurementsIndex = 0;
                layout.cachedLayout.widthMeasureMode = null;
                layout.cachedLayout.heightMeasureMode = null;
            }

            CSSCachedMeasurement cachedResults = null;

            // Determine whether the results are already cached. We maintain a separate
            // cache for layouts and measurements. A layout operation modifies the positions
            // and dimensions for nodes in the subtree. The algorithm assumes that each node
            // gets layed out a maximum of one time per tree layout, but multiple measurements
            // may be required to resolve all of the flex dimensions.
            // We handle nodes with measure functions specially here because they are the most
            // expensive to measure, so it's worth avoiding redundant measurements if at all possible.
            if (isMeasureDefined(node))
            {
                float marginAxisRow =
                    node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) +
                    node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]);
                float marginAxisColumn =
                    node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) +
                    node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]);

                // First, try to use the layout cache.
                if (canUseCachedMeasurement(node.IsTextNode, availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
                    widthMeasureMode, heightMeasureMode, layout.cachedLayout))
                {
                    cachedResults = layout.cachedLayout;
                }
                else
                {
                    // Try to use the measurement cache.
                    for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++)
                    {
                        if (canUseCachedMeasurement(node.IsTextNode, availableWidth, availableHeight, marginAxisRow, marginAxisColumn,
                            widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i]))
                        {
                            cachedResults = layout.cachedMeasurements[i];
                            break;
                        }
                    }
                }
            }
            else if (performLayout)
            {
                if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) &&
                    FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) &&
                    layout.cachedLayout.widthMeasureMode == widthMeasureMode &&
                    layout.cachedLayout.heightMeasureMode == heightMeasureMode)
                {

                    cachedResults = layout.cachedLayout;
                }
            }
            else
            {
                for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++)
                {
                    if (FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableWidth, availableWidth) &&
                        FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableHeight, availableHeight) &&
                        layout.cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
                        layout.cachedMeasurements[i].heightMeasureMode == heightMeasureMode)
                    {

                        cachedResults = layout.cachedMeasurements[i];
                        break;
                    }
                }
            }

            if (!needToVisitNode && cachedResults != null)
            {
                layout.measuredDimensions[DIMENSION_WIDTH] = cachedResults.computedWidth;
                layout.measuredDimensions[DIMENSION_HEIGHT] = cachedResults.computedHeight;
            }
            else
            {
                layoutNodeImpl(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);

                layout.lastParentDirection = parentDirection;

                if (cachedResults == null)
                {
                    if (layout.nextCachedMeasurementsIndex == CSSLayout.MAX_CACHED_RESULT_COUNT)
                    {
                        layout.nextCachedMeasurementsIndex = 0;
                    }

                    CSSCachedMeasurement newCacheEntry = null;
                    if (performLayout)
                    {
                        // Use the single layout cache entry.
                        newCacheEntry = layout.cachedLayout;
                    }
                    else
                    {
                        // Allocate a new measurement cache entry.
                        newCacheEntry = layout.cachedMeasurements[layout.nextCachedMeasurementsIndex];
                        if (newCacheEntry == null)
                        {
                            newCacheEntry = new CSSCachedMeasurement();
                            layout.cachedMeasurements[layout.nextCachedMeasurementsIndex] = newCacheEntry;
                        }
                        layout.nextCachedMeasurementsIndex++;
                    }

                    newCacheEntry.availableWidth = availableWidth;
                    newCacheEntry.availableHeight = availableHeight;
                    newCacheEntry.widthMeasureMode = widthMeasureMode;
                    newCacheEntry.heightMeasureMode = heightMeasureMode;
                    newCacheEntry.computedWidth = layout.measuredDimensions[DIMENSION_WIDTH];
                    newCacheEntry.computedHeight = layout.measuredDimensions[DIMENSION_HEIGHT];
                }
            }

            if (performLayout)
            {
                node.layout.dimensions[DIMENSION_WIDTH] = node.layout.measuredDimensions[DIMENSION_WIDTH];
                node.layout.dimensions[DIMENSION_HEIGHT] = node.layout.measuredDimensions[DIMENSION_HEIGHT];
                node.markHasNewLayout();
            }

            layout.generationCount = layoutContext.currentGenerationCount;
            return (needToVisitNode || cachedResults == null);
        }
        //
        // This is the main routine that implements a subset of the flexbox layout algorithm
        // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.
        //
        // Limitations of this algorithm, compared to the full standard:
        //  * Display property is always assumed to be 'flex' except for Text nodes, which
        //    are assumed to be 'inline-flex'.
        //  * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are
        //    stacked in document order.
        //  * The 'order' property is not supported. The order of flex items is always defined
        //    by document order.
        //  * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'
        //    and 'hidden' are not supported.
        //  * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The
        //    rarely-used 'wrap-reverse' is not supported.
        //  * Rather than allowing arbitrary combinations of flexGrow, flexShrink and
        //    flexBasis, this algorithm supports only the three most common combinations:
        //      flex: 0 is equiavlent to flex: 0 0 auto
        //      flex: n (where n is a positive value) is equivalent to flex: n 1 auto
        //          If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0
        //          This is faster because the content doesn't need to be measured, but it's
        //          less flexible because the basis is always 0 and can't be overriden with
        //          the width/height attributes.
        //      flex: -1 (or any negative value) is equivalent to flex: 0 1 auto
        //  * Margins cannot be specified as 'auto'. They must be specified in terms of pixel
        //    values, and the default value is 0.
        //  * The 'baseline' value is not supported for alignItems and alignSelf properties.
        //  * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be
        //    specified as pixel values, not as percentages.
        //  * There is no support for calculation of dimensions based on intrinsic aspect ratios
        //     (e.g. images).
        //  * There is no support for forced breaks.
        //  * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).
        //
        // Deviations from standard:
        //  * Section 4.5 of the spec indicates that all flex items have a default minimum
        //    main size. For text blocks, for example, this is the width of the widest word.
        //    Calculating the minimum width is expensive, so we forego it and assume a default
        //    minimum main size of 0.
        //  * Min/Max sizes in the main axis are not honored when resolving flexible lengths.
        //  * The spec indicates that the default value for 'flexDirection' is 'row', but
        //    the algorithm below assumes a default of 'column'.
        //
        // Input parameters:
        //    - node: current node to be sized and layed out
        //    - availableWidth & availableHeight: available size to be used for sizing the node
        //      or CSS_UNDEFINED if the size is not available; interpretation depends on layout
        //      flags
        //    - parentDirection: the inline (text) direction within the parent (left-to-right or
        //      right-to-left)
        //    - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)
        //    - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)
        //    - performLayout: specifies whether the caller is interested in just the dimensions
        //      of the node or it requires the entire node and its subtree to be layed out
        //      (with final positions)
        //
        // Details:
        //    This routine is called recursively to lay out subtrees of flexbox elements. It uses the
        //    information in node.style, which is treated as a read-only input. It is responsible for
        //    setting the layout.direction and layout.measured_dimensions fields for the input node as well
        //    as the layout.position and layout.line_index fields for its child nodes. The
        //    layout.measured_dimensions field includes any border or padding for the node but does
        //    not include margins.
        //
        //    The spec describes four different layout modes: "fill available", "max content", "min content",
        //    and "fit content". Of these, we don't use "min content" because we don't support default
        //    minimum main sizes (see above for details). Each of our measure modes maps to a layout mode
        //    from the spec (https://www.w3.org/TR/css3-sizing/#terms):
        //      - CSS_MEASURE_MODE_UNDEFINED: max content
        //      - CSS_MEASURE_MODE_EXACTLY: fill available
        //      - CSS_MEASURE_MODE_AT_MOST: fit content
        //
        //    When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of
        //    undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.
        //
        static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection, CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, boolean performLayout)
        {
            /** START_GENERATED **/

              Assertions.assertCondition(float.IsNaN(availableWidth) ? widthMeasureMode == CSSMeasureMode.Undefined : true, "availableWidth is indefinite so widthMeasureMode must be CSSMeasureMode.Undefined");
              Assertions.assertCondition(float.IsNaN(availableHeight) ? heightMeasureMode == CSSMeasureMode.Undefined : true, "availableHeight is indefinite so heightMeasureMode must be CSSMeasureMode.Undefined");

              float paddingAndBorderAxisRow = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])));
              float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
              float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
              float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));

              // Set the resolved resolution in the node's layout.
              CSSDirection direction = resolveDirection(node, parentDirection);
              node.layout.direction = direction;

              // For content (text) nodes, determine the dimensions based on the text contents.
              if (isMeasureDefined(node)) {
            float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;
            float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;

            if (widthMeasureMode == CSSMeasureMode.Exactly && heightMeasureMode == CSSMeasureMode.Exactly) {

              // Don't bother sizing the text if both dimensions are already defined.
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);
            } else if (innerWidth <= 0 || innerHeight <= 0) {

              // Don't bother sizing the text if there's no horizontal or vertical space.
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);
            } else {

              // Measure the text under the current constraints.
              MeasureOutput measureDim = node.measure(

            layoutContext.measureOutput,
            innerWidth,
            widthMeasureMode,
            innerHeight,
            heightMeasureMode
              );

              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW,
            (widthMeasureMode == CSSMeasureMode.Undefined || widthMeasureMode == CSSMeasureMode.AtMost) ?
              measureDim.width + paddingAndBorderAxisRow :
              availableWidth - marginAxisRow);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,
            (heightMeasureMode == CSSMeasureMode.Undefined || heightMeasureMode == CSSMeasureMode.AtMost) ?
              measureDim.height + paddingAndBorderAxisColumn :
              availableHeight - marginAxisColumn);
            }

            return;
              }

              // For nodes with no children, use the available values if they were provided, or
              // the minimum size as indicated by the padding and border sizes.
              int childCount = node.getChildCount();
              if (childCount == 0) {
            node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW,
              (widthMeasureMode == CSSMeasureMode.Undefined || widthMeasureMode == CSSMeasureMode.AtMost) ?
            paddingAndBorderAxisRow :
            availableWidth - marginAxisRow);
            node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,
              (heightMeasureMode == CSSMeasureMode.Undefined || heightMeasureMode == CSSMeasureMode.AtMost) ?
            paddingAndBorderAxisColumn :
            availableHeight - marginAxisColumn);
            return;
              }

              // If we're not being asked to perform a full layout, we can handle a number of common
              // cases here without incurring the cost of the remaining function.
              if (!performLayout) {
            // If we're being asked to size the content with an at most constraint but there is no available width,
            // the measurement will always be zero.
            if (widthMeasureMode == CSSMeasureMode.AtMost && availableWidth <= 0 &&
            heightMeasureMode == CSSMeasureMode.AtMost && availableHeight <= 0) {
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);
              return;
            }

            if (widthMeasureMode == CSSMeasureMode.AtMost && availableWidth <= 0) {
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, float.IsNaN(availableHeight) ? 0 : (availableHeight - marginAxisColumn));
              return;
            }

            if (heightMeasureMode == CSSMeasureMode.AtMost && availableHeight <= 0) {
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, float.IsNaN(availableWidth) ? 0 : (availableWidth - marginAxisRow));
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);
              return;
            }

            // If we're being asked to use an exact width/height, there's no need to measure the children.
            if (widthMeasureMode == CSSMeasureMode.Exactly && heightMeasureMode == CSSMeasureMode.Exactly) {
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);
              return;
            }
              }

              // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
              int mainAxis = resolveAxis(getFlexDirection(node), direction);
              int crossAxis = getCrossFlexDirection(mainAxis, direction);
              boolean isMainAxisRow = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE);
              CSSJustify justifyContent = node.style.justifyContent;
              boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap);

              CSSNode firstAbsoluteChild = null;
              CSSNode currentAbsoluteChild = null;

              float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]));
              float trailingPaddingAndBorderMain = (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
              float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]));
              float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
              float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));

              CSSMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;
              CSSMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;

              // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
              float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;
              float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;
              float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;
              float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;

              // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
              CSSNode child;
              int i;
              float childWidth;
              float childHeight;
              CSSMeasureMode childWidthMeasureMode;
              CSSMeasureMode childHeightMeasureMode;
              for (i = 0; i < childCount; i++) {
            child = node.getChildAt(i);

            if (performLayout) {
              // Set the initial position (relative to the parent).
              CSSDirection childDirection = resolveDirection(child, direction);
              setPosition(child, childDirection);
            }

            // Absolute-positioned children don't participate in flex layout. Add them
            // to a list that we can process later.
            if (child.style.positionType == CSSPositionType.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.nextChild = child;
              }
              currentAbsoluteChild = child;
              child.nextChild = null;
            } else {

              if (isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) {

            // The width is definite, so use that as the flex basis.
            child.layout.flexBasis = Math.Max(child.style.dimensions[DIMENSION_WIDTH], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]))));
              } else if (!isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {

            // The height is definite, so use that as the flex basis.
            child.layout.flexBasis = Math.Max(child.style.dimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))));
              } else if (!isFlexBasisAuto(child) && !float.IsNaN(availableInnerMainDim)) {

            // If the basis isn't 'auto', it is assumed to be zero.
            child.layout.flexBasis = Math.Max(0, ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))));
              } else {

            childWidth = CSSConstants.Undefined;
            childHeight = CSSConstants.Undefined;
            childWidthMeasureMode = CSSMeasureMode.Undefined;
            childHeightMeasureMode = CSSMeasureMode.Undefined;

            // Main axis
            if (isMainAxisRow) {
              if (widthMeasureMode == CSSMeasureMode.Undefined || float.IsNaN(availableInnerMainDim)) {
                childWidth = CSSConstants.Undefined;
                childWidthMeasureMode = CSSMeasureMode.Undefined;
              } else {
                childWidth = availableInnerMainDim;
                childWidthMeasureMode = CSSMeasureMode.AtMost;
              }
            } else if (node.style.overflow == CSSOverflow.Hidden) {
              if (heightMeasureMode == CSSMeasureMode.Undefined || float.IsNaN(availableInnerMainDim)) {
                childHeight = CSSConstants.Undefined;
                childHeightMeasureMode = CSSMeasureMode.Undefined;
              } else {
                childHeight = availableInnerMainDim;
                childHeightMeasureMode = CSSMeasureMode.AtMost;
              }
            }

            // Cross axis
            if (isMainAxisRow) {
              if (node.style.overflow == CSSOverflow.Hidden) {
                if (!float.IsNaN(availableInnerCrossDim) &&
                    !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) &&
                    heightMeasureMode == CSSMeasureMode.Exactly &&
                    getAlignItem(node, child) == CSSAlign.Stretch) {
                  childHeight = availableInnerCrossDim;
                  childHeightMeasureMode = CSSMeasureMode.Exactly;
                } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
                  childHeight = availableInnerCrossDim;
                  childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost;
                } else {
                  childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
                  childHeightMeasureMode = CSSMeasureMode.Exactly;
                }
              }
            } else {
              if (!float.IsNaN(availableInnerCrossDim) &&
                  !(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) &&
                  widthMeasureMode == CSSMeasureMode.Exactly &&
                  getAlignItem(node, child) == CSSAlign.Stretch) {
                childWidth = availableInnerCrossDim;
                childWidthMeasureMode = CSSMeasureMode.Exactly;
              } else if (!(child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) {
                childWidth = availableInnerCrossDim;
                childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost;
              } else {
                childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
                childWidthMeasureMode = CSSMeasureMode.Exactly;
              }
            }

            // Measure the child
            layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "measure");

            child.layout.flexBasis = Math.Max(isMainAxisRow ? child.layout.measuredDimensions[DIMENSION_WIDTH] : child.layout.measuredDimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))));
              }
            }
              }

              // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES

              // Indexes of children that represent the first and last items in the line.
              int startOfLineIndex = 0;
              int endOfLineIndex = 0;

              // Number of lines.
              int lineCount = 0;

              // Accumulated cross dimensions of all lines so far.
              float totalLineCrossDim = 0;

              // Max main dimension of all the lines.
              float maxLineMainDim = 0;

              while (endOfLineIndex < childCount) {

            // Number of items on the currently line. May be different than the difference
            // between start and end indicates because we skip over absolute-positioned items.
            int itemsOnLine = 0;

            // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin
            // of all the children on the current line. 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 sizeConsumedOnCurrentLine = 0;

            float totalFlexGrowFactors = 0;
            float totalFlexShrinkScaledFactors = 0;

            i = startOfLineIndex;

            // Maintain a linked list of the child nodes that can shrink and/or grow.
            CSSNode firstRelativeChild = null;
            CSSNode currentRelativeChild = null;

            // Add items to the current line until it's full or we run out of items.
            while (i < childCount) {
              child = node.getChildAt(i);
              child.lineIndex = lineCount;

              if (child.style.positionType != CSSPositionType.Absolute) {
            float outerFlexBasis = child.layout.flexBasis + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));

            // If this is a multi-line flow and this item pushes us over the available size, we've
            // hit the end of the current line. Break out of the loop and lay out the current line.
            if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {
              break;
            }

            sizeConsumedOnCurrentLine += outerFlexBasis;
            itemsOnLine++;

            if ((child.style.positionType == CSSPositionType.Relative && child.style.flex != 0)) {
              totalFlexGrowFactors += getFlexGrowFactor(child);

              // Unlike the grow factor, the shrink factor is scaled relative to the child
              // dimension.
              totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;
            }

            // Store a private linked list of children that need to be layed out.
            if (firstRelativeChild == null) {
              firstRelativeChild = child;
            }
            if (currentRelativeChild != null) {
              currentRelativeChild.nextChild = child;
            }
            currentRelativeChild = child;
            child.nextChild = null;
              }

              i++;
              endOfLineIndex++;
            }

            // If we don't need to measure the cross axis, we can skip the entire flex step.
            boolean canSkipFlex = !performLayout && measureModeCrossDim == CSSMeasureMode.Exactly;

            // 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;

            // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
            // Calculate the remaining available space that needs to be allocated.
            // If the main dimension size isn't known, it is computed based on
            // the line length, so there's no more space left to distribute.
            float remainingFreeSpace = 0;
            if (!float.IsNaN(availableInnerMainDim)) {
              remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;
            } else if (sizeConsumedOnCurrentLine < 0) {
              // availableInnerMainDim is indefinite which means the node is being sized based on its content.
              // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for
              // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
              remainingFreeSpace = -sizeConsumedOnCurrentLine;
            }

            float originalRemainingFreeSpace = remainingFreeSpace;
            float deltaFreeSpace = 0;

            if (!canSkipFlex) {
              float childFlexBasis;
              float flexShrinkScaledFactor;
              float flexGrowFactor;
              float baseMainSize;
              float boundMainSize;

              // Do two passes over the flex items to figure out how to distribute the remaining space.
              // The first pass finds the items whose min/max constraints trigger, freezes them at those
              // sizes, and excludes those sizes from the remaining space. The second pass sets the size
              // of each flexible item. It distributes the remaining space amongst the items whose min/max
              // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing
              // their min/max constraints to trigger again.
              //
              // This two pass approach for resolving min/max constraints deviates from the spec. The
              // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process
              // that needs to be repeated a variable number of times. The algorithm implemented here
              // won't handle all cases but it was simpler to implement and it mitigates performance
              // concerns because we know exactly how many passes it'll do.

              // First pass: detect the flex items whose min/max constraints trigger
              float deltaFlexShrinkScaledFactors = 0;
              float deltaFlexGrowFactors = 0;
              currentRelativeChild = firstRelativeChild;
              while (currentRelativeChild != null) {
            childFlexBasis = currentRelativeChild.layout.flexBasis;

            if (remainingFreeSpace < 0) {
              flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;

              // Is this child able to shrink?
              if (flexShrinkScaledFactor != 0) {
                baseMainSize = childFlexBasis +
                  remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;
                boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);
                if (baseMainSize != boundMainSize) {
                  // By excluding this item's size and flex factor from remaining, this item's
                  // min/max constraints should also trigger in the second pass resulting in the
                  // item's size calculation being identical in the first and second passes.
                  deltaFreeSpace -= boundMainSize - childFlexBasis;
                  deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;
                }
              }
            } else if (remainingFreeSpace > 0) {
              flexGrowFactor = getFlexGrowFactor(currentRelativeChild);

              // Is this child able to grow?
              if (flexGrowFactor != 0) {
                baseMainSize = childFlexBasis +
                  remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;
                boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);
                if (baseMainSize != boundMainSize) {
                  // By excluding this item's size and flex factor from remaining, this item's
                  // min/max constraints should also trigger in the second pass resulting in the
                  // item's size calculation being identical in the first and second passes.
                  deltaFreeSpace -= boundMainSize - childFlexBasis;
                  deltaFlexGrowFactors -= flexGrowFactor;
                }
              }
            }

            currentRelativeChild = currentRelativeChild.nextChild;
              }

              totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;
              totalFlexGrowFactors += deltaFlexGrowFactors;
              remainingFreeSpace += deltaFreeSpace;

              // Second pass: resolve the sizes of the flexible items
              deltaFreeSpace = 0;
              currentRelativeChild = firstRelativeChild;
              while (currentRelativeChild != null) {
            childFlexBasis = currentRelativeChild.layout.flexBasis;
            float updatedMainSize = childFlexBasis;

            if (remainingFreeSpace < 0) {
              flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;

              // Is this child able to shrink?
              if (flexShrinkScaledFactor != 0) {
                updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +
                  remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);
              }
            } else if (remainingFreeSpace > 0) {
              flexGrowFactor = getFlexGrowFactor(currentRelativeChild);

              // Is this child able to grow?
              if (flexGrowFactor != 0) {
                updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +
                  remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);
              }
            }

            deltaFreeSpace -= updatedMainSize - childFlexBasis;

            if (isMainAxisRow) {
              childWidth = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
              childWidthMeasureMode = CSSMeasureMode.Exactly;

              if (!float.IsNaN(availableInnerCrossDim) &&
                  !(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) &&
                  heightMeasureMode == CSSMeasureMode.Exactly &&
                  getAlignItem(node, currentRelativeChild) == CSSAlign.Stretch) {
                childHeight = availableInnerCrossDim;
                childHeightMeasureMode = CSSMeasureMode.Exactly;
              } else if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
                childHeight = availableInnerCrossDim;
                childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost;
              } else {
                childHeight = currentRelativeChild.style.dimensions[DIMENSION_HEIGHT] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
                childHeightMeasureMode = CSSMeasureMode.Exactly;
              }
            } else {
              childHeight = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
              childHeightMeasureMode = CSSMeasureMode.Exactly;

              if (!float.IsNaN(availableInnerCrossDim) &&
                  !(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0) &&
                  widthMeasureMode == CSSMeasureMode.Exactly &&
                  getAlignItem(node, currentRelativeChild) == CSSAlign.Stretch) {
                childWidth = availableInnerCrossDim;
                childWidthMeasureMode = CSSMeasureMode.Exactly;
              } else if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) {
                childWidth = availableInnerCrossDim;
                childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost;
              } else {
                childWidth = currentRelativeChild.style.dimensions[DIMENSION_WIDTH] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
                childWidthMeasureMode = CSSMeasureMode.Exactly;
              }
            }

            boolean requiresStretchLayout = !(currentRelativeChild.style.dimensions[dim[crossAxis]] >= 0.0) &&
              getAlignItem(node, currentRelativeChild) == CSSAlign.Stretch;

            // Recursively call the layout algorithm for this child with the updated main size.
            layoutNodeInternal(layoutContext, currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, "flex");

            currentRelativeChild = currentRelativeChild.nextChild;
              }
            }

            remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;

            // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION

            // At this point, all the children have their dimensions set in the main axis.
            // Their dimensions are also set in the cross axis with the exception of items
            // that are aligned "stretch". We need to compute these stretch values and
            // set the final positions.

            // If we are using "at most" rules in the main axis, we won't distribute
            // any remaining space at this point.
            if (measureModeMainDim == CSSMeasureMode.AtMost) {
              remainingFreeSpace = 0;
            }

            // Use justifyContent to figure out how to allocate the remaining space
            // available in the main axis.
            if (justifyContent != CSSJustify.FlexStart) {
              if (justifyContent == CSSJustify.Center) {
            leadingMainDim = remainingFreeSpace / 2;
              } else if (justifyContent == CSSJustify.FlexEnd) {
            leadingMainDim = remainingFreeSpace;
              } else if (justifyContent == CSSJustify.SpaceBetween) {
            remainingFreeSpace = Math.Max(remainingFreeSpace, 0);
            if (itemsOnLine > 1) {
              betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);
            } else {
              betweenMainDim = 0;
            }
              } else if (justifyContent == CSSJustify.SpaceAround) {
            // Space on the edges is half of the space between elements
            betweenMainDim = remainingFreeSpace / itemsOnLine;
            leadingMainDim = betweenMainDim / 2;
              }
            }

            float mainDim = leadingPaddingAndBorderMain + leadingMainDim;
            float crossDim = 0;

            for (i = startOfLineIndex; i < endOfLineIndex; ++i) {
              child = node.getChildAt(i);

              if (child.style.positionType == CSSPositionType.Absolute &&
              !float.IsNaN(child.style.position[leading[mainAxis]])) {
            if (performLayout) {
              // 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]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ?  0 : child.style.position[leading[mainAxis]]) +
                node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
                child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]);
            }
              } else {
            if (performLayout) {
              // 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;
            }

            // Now that we placed the element, we need to update the variables.
            // We need to do that only for relative elements. Absolute elements
            // do not take part in that phase.
            if (child.style.positionType == CSSPositionType.Relative) {
              if (canSkipFlex) {
                // If we skipped the flex step, then we can't rely on the measuredDims because
                // they weren't computed. This means we can't call getDimWithMargin.
                mainDim += betweenMainDim + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])) + child.layout.flexBasis;
                crossDim = availableInnerCrossDim;
              } else {
                // The main dimension is the sum of all the elements dimension plus
                // the spacing.
                mainDim += betweenMainDim + (child.layout.measuredDimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));

                // The cross dimension is the max of the elements dimension since there
                // can only be one element in that cross dimension.
                crossDim = Math.Max(crossDim, (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
              }
            }
              }
            }

            mainDim += trailingPaddingAndBorderMain;

            float containerCrossAxis = availableInnerCrossDim;
            if (measureModeCrossDim == CSSMeasureMode.Undefined || measureModeCrossDim == CSSMeasureMode.AtMost) {
              // Compute the cross axis from the max cross dimension of the children.
              containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;

              if (measureModeCrossDim == CSSMeasureMode.AtMost) {
            containerCrossAxis = Math.Min(containerCrossAxis, availableInnerCrossDim);
              }
            }

            // If there's no flex wrap, the cross dimension is defined by the container.
            if (!isNodeFlexWrap && measureModeCrossDim == CSSMeasureMode.Exactly) {
              crossDim = availableInnerCrossDim;
            }

            // Clamp to the min/max size specified on the container.
            crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;

            // STEP 7: CROSS-AXIS ALIGNMENT
            // We can skip child alignment if we're just measuring the container.
            if (performLayout) {
              for (i = startOfLineIndex; i < endOfLineIndex; ++i) {
            child = node.getChildAt(i);

            if (child.style.positionType == CSSPositionType.Absolute) {
              // If the child is absolutely positioned and has a top/left/bottom/right
              // set, override all the previously computed positions to set it correctly.
              if (!float.IsNaN(child.style.position[leading[crossAxis]])) {
                child.layout.position[pos[crossAxis]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ?  0 : child.style.position[leading[crossAxis]]) +
                  node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
                  child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
              } else {
                child.layout.position[pos[crossAxis]] = leadingPaddingAndBorderCross +
                  child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[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
              CSSAlign alignItem = getAlignItem(node, child);

              // If the child uses align stretch, we need to lay it out one more time, this time
              // forcing the cross-axis size to be the computed cross size for the current line.
              if (alignItem == CSSAlign.Stretch) {
                childWidth = child.layout.measuredDimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
                childHeight = child.layout.measuredDimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
                boolean isCrossSizeDefinite = false;

                if (isMainAxisRow) {
                  isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0);
                  childHeight = crossDim;
                } else {
                  isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0);
                  childWidth = crossDim;
                }

                // If the child defines a definite size for its cross axis, there's no need to stretch.
                if (!isCrossSizeDefinite) {
                  childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly;
                  childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly;
                  layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, "stretch");
                }
              } else if (alignItem != CSSAlign.FlexStart) {
                float remainingCrossDim = containerCrossAxis - (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]));

                if (alignItem == CSSAlign.Center) {
                  leadingCrossDim += remainingCrossDim / 2;
                } else { // CSSAlign.FlexEnd
                  leadingCrossDim += remainingCrossDim;
                }
              }

              // And we apply the position
              child.layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;
            }
              }
            }

            totalLineCrossDim += crossDim;
            maxLineMainDim = Math.Max(maxLineMainDim, mainDim);

            // Reset variables for new line.
            lineCount++;
            startOfLineIndex = endOfLineIndex;
            endOfLineIndex = startOfLineIndex;
              }

              // STEP 8: MULTI-LINE CONTENT ALIGNMENT
              if (lineCount > 1 && performLayout && !float.IsNaN(availableInnerCrossDim)) {
            float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;

            float crossDimLead = 0;
            float currentLead = leadingPaddingAndBorderCross;

            CSSAlign alignContent = node.style.alignContent;
            if (alignContent == CSSAlign.FlexEnd) {
              currentLead += remainingAlignContentDim;
            } else if (alignContent == CSSAlign.Center) {
              currentLead += remainingAlignContentDim / 2;
            } else if (alignContent == CSSAlign.Stretch) {
              if (availableInnerCrossDim > totalLineCrossDim) {
            crossDimLead = (remainingAlignContentDim / lineCount);
              }
            }

            int endIndex = 0;
            for (i = 0; i < lineCount; ++i) {
              int startIndex = endIndex;
              int j;

              // compute the line's height and find the endIndex
              float lineHeight = 0;
              for (j = startIndex; j < childCount; ++j) {
            child = node.getChildAt(j);
            if (child.style.positionType != CSSPositionType.Relative) {
              continue;
            }
            if (child.lineIndex != i) {
              break;
            }
            if ((child.layout.measuredDimensions[dim[crossAxis]] >= 0.0)) {
              lineHeight = Math.Max(lineHeight,
                child.layout.measuredDimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
            }
              }
              endIndex = j;
              lineHeight += crossDimLead;

              if (performLayout) {
            for (j = startIndex; j < endIndex; ++j) {
              child = node.getChildAt(j);
              if (child.style.positionType != CSSPositionType.Relative) {
                continue;
              }

              CSSAlign alignContentAlignItem = getAlignItem(node, child);
              if (alignContentAlignItem == CSSAlign.FlexStart) {
                child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
              } else if (alignContentAlignItem == CSSAlign.FlexEnd) {
                child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.measuredDimensions[dim[crossAxis]];
              } else if (alignContentAlignItem == CSSAlign.Center) {
                childHeight = child.layout.measuredDimensions[dim[crossAxis]];
                child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
              } else if (alignContentAlignItem == CSSAlign.Stretch) {
                child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
                // TODO(prenaux): Correctly set the height of items with indefinite
                //                (auto) crossAxis dimension.
              }
            }
              }

              currentLead += lineHeight;
            }
              }

              // STEP 9: COMPUTING FINAL DIMENSIONS
              node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);
              node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);

              // If the user didn't specify a width or height for the node, set the
              // dimensions based on the children.
              if (measureModeMainDim == CSSMeasureMode.Undefined) {
            // Clamp the size to the min/max size, if specified, and make sure it
            // doesn't go below the padding and border amount.
            node.layout.measuredDimensions[dim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);
              } else if (measureModeMainDim == CSSMeasureMode.AtMost) {
            node.layout.measuredDimensions[dim[mainAxis]] = Math.Max(
              Math.Min(availableInnerMainDim + paddingAndBorderAxisMain,
            boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),
              paddingAndBorderAxisMain);
              }

              if (measureModeCrossDim == CSSMeasureMode.Undefined) {
            // Clamp the size to the min/max size, if specified, and make sure it
            // doesn't go below the padding and border amount.
            node.layout.measuredDimensions[dim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);
              } else if (measureModeCrossDim == CSSMeasureMode.AtMost) {
            node.layout.measuredDimensions[dim[crossAxis]] = Math.Max(
              Math.Min(availableInnerCrossDim + paddingAndBorderAxisCross,
            boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),
              paddingAndBorderAxisCross);
              }

              // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN
              if (performLayout) {
            boolean needsMainTrailingPos = false;
            boolean needsCrossTrailingPos = false;

            if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
            mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
              needsMainTrailingPos = true;
            }

            if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
            crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
              needsCrossTrailingPos = true;
            }

            // Set trailing position if necessary.
            if (needsMainTrailingPos || needsCrossTrailingPos) {
              for (i = 0; i < childCount; ++i) {
            child = node.getChildAt(i);

            if (needsMainTrailingPos) {
              child.layout.position[trailing[mainAxis]] = node.layout.measuredDimensions[dim[mainAxis]] - (child.style.positionType == CSSPositionType.Absolute ? 0 : child.layout.measuredDimensions[dim[mainAxis]]) - child.layout.position[pos[mainAxis]];
            }

            if (needsCrossTrailingPos) {
              child.layout.position[trailing[crossAxis]] = node.layout.measuredDimensions[dim[crossAxis]] - (child.style.positionType == CSSPositionType.Absolute ? 0 : child.layout.measuredDimensions[dim[crossAxis]]) - child.layout.position[pos[crossAxis]];
            }
              }
            }
              }

              // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN
              currentAbsoluteChild = firstAbsoluteChild;
              while (currentAbsoluteChild != null) {
            // Now that we know the bounds of the container, perform layout again on the
            // absolutely-positioned children.
            if (performLayout) {

              childWidth = CSSConstants.Undefined;
              childHeight = CSSConstants.Undefined;

              if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) {
            childWidth = currentAbsoluteChild.style.dimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
              } else {
            // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.
            if (!float.IsNaN(currentAbsoluteChild.style.position[POSITION_LEFT]) && !float.IsNaN(currentAbsoluteChild.style.position[POSITION_RIGHT])) {
              childWidth = node.layout.measuredDimensions[DIMENSION_WIDTH] -
                (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])) -
                (currentAbsoluteChild.style.position[POSITION_LEFT] + currentAbsoluteChild.style.position[POSITION_RIGHT]);
              childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);
            }
              }

              if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
            childHeight = currentAbsoluteChild.style.dimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
              } else {
            // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.
            if (!float.IsNaN(currentAbsoluteChild.style.position[POSITION_TOP]) && !float.IsNaN(currentAbsoluteChild.style.position[POSITION_BOTTOM])) {
              childHeight = node.layout.measuredDimensions[DIMENSION_HEIGHT] -
                (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) -
                (currentAbsoluteChild.style.position[POSITION_TOP] + currentAbsoluteChild.style.position[POSITION_BOTTOM]);
              childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);
            }
              }

              // If we're still missing one or the other dimension, measure the content.
              if (float.IsNaN(childWidth) || float.IsNaN(childHeight)) {
            childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly;
            childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly;

            // According to the spec, if the main size is not definite and the
            // child's inline axis is parallel to the main axis (i.e. it's
            // horizontal), the child should be sized using "UNDEFINED" in
            // the main size. Otherwise use "AT_MOST" in the cross axis.
            if (!isMainAxisRow && float.IsNaN(childWidth) && !float.IsNaN(availableInnerWidth)) {
              childWidth = availableInnerWidth;
              childWidthMeasureMode = CSSMeasureMode.AtMost;
            }

            // The W3C spec doesn't say anything about the 'overflow' property,
            // but all major browsers appear to implement the following logic.
            if (node.style.overflow == CSSOverflow.Hidden) {
              if (isMainAxisRow && float.IsNaN(childHeight) && !float.IsNaN(availableInnerHeight)) {
                childHeight = availableInnerHeight;
                childHeightMeasureMode = CSSMeasureMode.AtMost;
              }
            }

            layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "abs-measure");
            childWidth = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]));
            childHeight = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]));
              }

              layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, CSSMeasureMode.Exactly, CSSMeasureMode.Exactly, true, "abs-layout");

              if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) &&
              !!float.IsNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_ROW]])) {
            currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_ROW]] =
              node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
              currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
              (float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) ?  0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]);
              }

              if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) &&
              !!float.IsNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_COLUMN]])) {
            currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_COLUMN]] =
              node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
              currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
              (float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) ?  0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]);
              }
            }

            currentAbsoluteChild = currentAbsoluteChild.nextChild;
              }
              /** END_GENERATED **/
        }
Exemple #18
0
        static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, float parentMaxHeight, CSSDirection? parentDirection)
        {
            var childCount_ = node.getChildCount();
            for (int i_ = 0; i_ < childCount_; i_++)
            {
                node.getChildAt(i_).layout.resetResult();
            }


            /** START_GENERATED **/
    
      CSSDirection direction = resolveDirection(node, parentDirection);
      int mainAxis = resolveAxis(getFlexDirection(node), direction);
      int crossAxis = getCrossFlexDirection(mainAxis, direction);
      int 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]] += node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
        getRelativePosition(node, mainAxis);
      node.layout.position[trailing[mainAxis]] += node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) +
        getRelativePosition(node, mainAxis);
      node.layout.position[leading[crossAxis]] += node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
        getRelativePosition(node, crossAxis);
      node.layout.position[trailing[crossAxis]] += node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) +
        getRelativePosition(node, crossAxis);
    
      // Inline immutable values from the target node to avoid excessive method
      // invocations during the layout calculation.
      int childCount = node.getChildCount();
      float paddingAndBorderAxisResolvedRow = ((node.style.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) + (node.style.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) + node.style.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])));
      float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
    
      if (isMeasureDefined(node)) {
        boolean isResolvedRowDimDefined = !float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]);
    
        float width = CSSConstants.Undefined;
        if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
          width = node.style.dimensions[DIMENSION_WIDTH];
        } else if (isResolvedRowDimDefined) {
          width = node.layout.dimensions[dim[resolvedRowAxis]];
        } else {
          width = parentMaxWidth -
            (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]));
        }
        width -= paddingAndBorderAxisResolvedRow;
    
        float height = CSSConstants.Undefined;
        if ((!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
          height = node.style.dimensions[DIMENSION_HEIGHT];
        } else if (!float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]])) {
          height = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]];
        } else {
          height = parentMaxHeight -
            (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]));
        }
        height -= ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])));
    
        // 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.
        boolean isRowUndefined = !(!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0) && !isResolvedRowDimDefined;
        boolean isColumnUndefined = !(!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) &&
          float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
    
        // Let's not measure the text if we already know both dimensions
        if (isRowUndefined || isColumnUndefined) {
          MeasureOutput measureDim = node.measure(
            
            layoutContext.measureOutput,
            width,
            height
          );
          if (isRowUndefined) {
            node.layout.dimensions[DIMENSION_WIDTH] = measureDim.width +
              paddingAndBorderAxisResolvedRow;
          }
          if (isColumnUndefined) {
            node.layout.dimensions[DIMENSION_HEIGHT] = measureDim.height +
              paddingAndBorderAxisColumn;
          }
        }
        if (childCount == 0) {
          return;
        }
      }
    
      boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap);
    
      CSSJustify justifyContent = node.style.justifyContent;
    
      float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]));
      float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]));
      float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
      float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])));
    
      boolean isMainDimDefined = !float.IsNaN(node.layout.dimensions[dim[mainAxis]]);
      boolean isCrossDimDefined = !float.IsNaN(node.layout.dimensions[dim[crossAxis]]);
      boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE);
    
      int i;
      int ii;
      CSSNode child;
      int axis;
    
      CSSNode firstAbsoluteChild = null;
      CSSNode currentAbsoluteChild = null;
    
      float definedMainDim = CSSConstants.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>.
        boolean isSimpleStackMain =
            (isMainDimDefined && justifyContent == CSSJustify.FlexStart) ||
            (!isMainDimDefined && justifyContent != CSSJustify.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>.
        boolean isSimpleStackCross = true;
        int firstComplexCross = childCount;
    
        CSSNode firstFlexChild = null;
        CSSNode currentFlexChild = null;
    
        float mainDim = leadingPaddingAndBorderMain;
        float crossDim = 0;
    
        float maxWidth;
        float maxHeight;
        for (i = startLine; i < childCount; ++i) {
          child = node.getChildAt(i);
          child.lineIndex = linesCount;
    
          child.nextAbsoluteChild = null;
          child.nextFlexChild = null;
    
          CSSAlign alignItem = getAlignItem(node, child);
    
          // Pre-fill cross axis dimensions when the child is using stretch before
          // we call the recursive layout pass
          if (alignItem == CSSAlign.Stretch &&
              child.style.positionType == CSSPositionType.Relative &&
              isCrossDimDefined &&
              !(!float.IsNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) {
            child.layout.dimensions[dim[crossAxis]] = Math.Max(
              boundAxis(child, crossAxis, node.layout.dimensions[dim[crossAxis]] -
                paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
              // You never want to go smaller than padding
              ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
            );
          } else if (child.style.positionType == CSSPositionType.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.nextAbsoluteChild = 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 (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
                  !(!float.IsNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] >= 0.0) &&
                  !float.IsNaN(child.style.position[leading[axis]]) &&
                  !float.IsNaN(child.style.position[trailing[axis]])) {
                child.layout.dimensions[dim[axis]] = Math.Max(
                  boundAxis(child, axis, node.layout.dimensions[dim[axis]] -
                    ((node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) -
                    (child.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                    (float.IsNaN(child.style.position[leading[axis]]) ?  0 : child.style.position[leading[axis]]) -
                    (float.IsNaN(child.style.position[trailing[axis]]) ?  0 : child.style.position[trailing[axis]])),
                  // You never want to go smaller than padding
                  ((child.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
                );
              }
            }
          }
    
          float nextContentDim = 0;
    
          // It only makes sense to consider a child flexible if we have a computed
          // dimension for the node.
          if (isMainDimDefined && (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0)) {
            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.nextFlexChild = 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 = ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) +
              (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
    
          } else {
            maxWidth = CSSConstants.Undefined;
            maxHeight = CSSConstants.Undefined;
    
            if (!isMainRowDirection) {
              if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
                maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
                  paddingAndBorderAxisResolvedRow;
              } else {
                maxWidth = parentMaxWidth -
                  (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
                  paddingAndBorderAxisResolvedRow;
              }
            } else {
              if ((!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
                maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
                    paddingAndBorderAxisColumn;
              } else {
                maxHeight = parentMaxHeight -
                  (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) -
                  paddingAndBorderAxisColumn;
              }
            }
    
            // This is the main recursive call. We layout non flexible children.
            if (alreadyComputedNextLayout == 0) {
              layoutNode(layoutContext, 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.positionType == CSSPositionType.Relative) {
              nonFlexibleChildrenCount++;
              // At this point we know the final size and margin of the element.
              nextContentDim = (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[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.positionType != CSSPositionType.Relative || (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0))) {
            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.positionType != CSSPositionType.Relative ||
                  (alignItem != CSSAlign.Stretch && alignItem != CSSAlign.FlexStart) ||
                  float.IsNaN(child.layout.dimensions[dim[crossAxis]]))) {
            isSimpleStackCross = false;
            firstComplexCross = i;
          }
    
          if (isSimpleStackMain) {
            child.layout.position[pos[mainAxis]] += mainDim;
            if (isMainDimDefined) {
              child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
            }
    
            mainDim += (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
            crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
          }
    
          if (isSimpleStackCross) {
            child.layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
            if (isCrossDimDefined) {
              child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[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 = Math.Max(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 +
                ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])));
            boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
    
            if (baseMainDim != boundMainDim) {
              remainingMainDim -= boundMainDim;
              totalFlexible -= currentFlexChild.style.flex;
            }
    
            currentFlexChild = currentFlexChild.nextFlexChild;
          }
          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 +
                  ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))
            );
    
            maxWidth = CSSConstants.Undefined;
            if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) {
              maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] -
                paddingAndBorderAxisResolvedRow;
            } else if (!isMainRowDirection) {
              maxWidth = parentMaxWidth -
                (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) -
                paddingAndBorderAxisResolvedRow;
            }
            maxHeight = CSSConstants.Undefined;
            if ((!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) {
              maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
                paddingAndBorderAxisColumn;
            } else if (isMainRowDirection) {
              maxHeight = parentMaxHeight -
                (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) -
                paddingAndBorderAxisColumn;
            }
    
            // And we recursively call the layout algorithm for this child
            layoutNode(layoutContext, currentFlexChild, maxWidth, maxHeight, direction);
    
            child = currentFlexChild;
            currentFlexChild = currentFlexChild.nextFlexChild;
            child.nextFlexChild = null;
          }
    
        // We use justifyContent to figure out how to allocate the remaining
        // space available
        } else if (justifyContent != CSSJustify.FlexStart) {
          if (justifyContent == CSSJustify.Center) {
            leadingMainDim = remainingMainDim / 2;
          } else if (justifyContent == CSSJustify.FlexEnd) {
            leadingMainDim = remainingMainDim;
          } else if (justifyContent == CSSJustify.SpaceBetween) {
            remainingMainDim = Math.Max(remainingMainDim, 0);
            if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
              betweenMainDim = remainingMainDim /
                (flexibleChildrenCount + nonFlexibleChildrenCount - 1);
            } else {
              betweenMainDim = 0;
            }
          } else if (justifyContent == CSSJustify.SpaceAround) {
            // 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.getChildAt(i);
    
          if (child.style.positionType == CSSPositionType.Absolute &&
              !float.IsNaN(child.style.position[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]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ?  0 : child.style.position[leading[mainAxis]]) +
              node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) +
              child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[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) {
              child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[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.positionType == CSSPositionType.Relative) {
              // The main dimension is the sum of all the elements dimension plus
              // the spacing.
              mainDim += betweenMainDim + (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]));
              // The cross dimension is the max of the elements dimension since there
              // can only be one element in that cross dimension.
              crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))));
            }
          }
        }
    
        float containerCrossAxis = node.layout.dimensions[dim[crossAxis]];
        if (!isCrossDimDefined) {
          containerCrossAxis = Math.Max(
            // 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.getChildAt(i);
    
          if (child.style.positionType == CSSPositionType.Absolute &&
              !float.IsNaN(child.style.position[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]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ?  0 : child.style.position[leading[crossAxis]]) +
              node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) +
              child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[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.positionType == CSSPositionType.Relative) {
              /*eslint-disable */
              // This variable is intentionally re-defined as the code is transpiled to a block scope language
              CSSAlign alignItem = getAlignItem(node, child);
              /*eslint-enable */
              if (alignItem == CSSAlign.Stretch) {
                // You can only stretch if the dimension has not already been set
                // previously.
                if (float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
                  child.layout.dimensions[dim[crossAxis]] = Math.Max(
                    boundAxis(child, crossAxis, containerCrossAxis -
                      paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))),
                    // You never want to go smaller than padding
                    ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))
                  );
                }
              } else if (alignItem != CSSAlign.FlexStart) {
                // The remaining space between the parent dimensions+padding and child
                // dimensions+margin.
                float remainingCrossDim = containerCrossAxis -
                  paddingAndBorderAxisCross - (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]));
    
                if (alignItem == CSSAlign.Center) {
                  leadingCrossDim += remainingCrossDim / 2;
                } else { // CSSAlign.FlexEnd
                  leadingCrossDim += remainingCrossDim;
                }
              }
            }
    
            // And we apply the position
            child.layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
    
            // Define the trailing position accordingly.
            if (isCrossDimDefined) {
              child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]];
            }
          }
        }
    
        linesCrossDim += crossDim;
        linesMainDim = Math.Max(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;
    
        CSSAlign alignContent = node.style.alignContent;
        if (alignContent == CSSAlign.FlexEnd) {
          currentLead += remainingAlignContentDim;
        } else if (alignContent == CSSAlign.Center) {
          currentLead += remainingAlignContentDim / 2;
        } else if (alignContent == CSSAlign.Stretch) {
          if (nodeCrossAxisInnerSize > linesCrossDim) {
            crossDimLead = (remainingAlignContentDim / linesCount);
          }
        }
    
        int endIndex = 0;
        for (i = 0; i < linesCount; ++i) {
          int startIndex = endIndex;
    
          // compute the line's height and find the endIndex
          float lineHeight = 0;
          for (ii = startIndex; ii < childCount; ++ii) {
            child = node.getChildAt(ii);
            if (child.style.positionType != CSSPositionType.Relative) {
              continue;
            }
            if (child.lineIndex != i) {
              break;
            }
            if (!float.IsNaN(child.layout.dimensions[dim[crossAxis]])) {
              lineHeight = Math.Max(
                lineHeight,
                child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))
              );
            }
          }
          endIndex = ii;
          lineHeight += crossDimLead;
    
          for (ii = startIndex; ii < endIndex; ++ii) {
            child = node.getChildAt(ii);
            if (child.style.positionType != CSSPositionType.Relative) {
              continue;
            }
    
            CSSAlign alignContentAlignItem = getAlignItem(node, child);
            if (alignContentAlignItem == CSSAlign.FlexStart) {
              child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
            } else if (alignContentAlignItem == CSSAlign.FlexEnd) {
              child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.dimensions[dim[crossAxis]];
            } else if (alignContentAlignItem == CSSAlign.Center) {
              float childHeight = child.layout.dimensions[dim[crossAxis]];
              child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
            } else if (alignContentAlignItem == CSSAlign.Stretch) {
              child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]);
              // TODO(prenaux): Correctly set the height of items with undefined
              //                (auto) crossAxis dimension.
            }
          }
    
          currentLead += lineHeight;
        }
      }
    
      boolean needsMainTrailingPos = false;
      boolean 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]] = Math.Max(
          // We're missing the last padding at this point to get the final
          // dimension
          boundAxis(node, mainAxis, linesMainDim + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[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]] = Math.Max(
          // 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.getChildAt(i);
    
          if (needsMainTrailingPos) {
            child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]];
          }
    
          if (needsCrossTrailingPos) {
            child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[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 (!float.IsNaN(node.layout.dimensions[dim[axis]]) &&
              !(!float.IsNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] >= 0.0) &&
              !float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) &&
              !float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]])) {
            currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.Max(
              boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] -
                (node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) -
                (float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) ?  0 : currentAbsoluteChild.style.position[leading[axis]]) -
                (float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ?  0 : currentAbsoluteChild.style.position[trailing[axis]])
              ),
              // You never want to go smaller than padding
              ((currentAbsoluteChild.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])))
            );
          }
    
          if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) &&
              !!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]])) {
            currentAbsoluteChild.layout.position[leading[axis]] =
              node.layout.dimensions[dim[axis]] -
              currentAbsoluteChild.layout.dimensions[dim[axis]] -
              (float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ?  0 : currentAbsoluteChild.style.position[trailing[axis]]);
          }
        }
    
        child = currentAbsoluteChild;
        currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
        child.nextAbsoluteChild = null;
      }
    }
Exemple #19
0
 public void SetDirection(CSSDirection direction)
 {
     if (this.style.Direction != direction) {
         this.style.Direction = direction;
         Dirty ();
     }
 }
Exemple #20
0
 public static extern void CSSNodeCalculateLayout(IntPtr node,
                                                  float availableWidth,
                                                  float availableHeight,
                                                  CSSDirection parentDirection);
Exemple #21
0
 public static extern void CSSNodeStyleSetDirection(IntPtr node, CSSDirection direction);