mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 09:40:21 +00:00
1326 lines
59 KiB
ReasonML
1326 lines
59 KiB
ReasonML
/**
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
/*
|
|
* From css-layout comments:
|
|
* 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):
|
|
*
|
|
* -.CssMeasureModeUndefined: `max-content`
|
|
* -.CssMeasureModeExactly: `fill-available`
|
|
* -.CssMeasureModeAtMost: `fit-content`
|
|
* If infinite space available in that axis, then `max-content.`
|
|
* Else, `min(max-content size, max(min-content size, fill-available size))`
|
|
* (Although, we don't support min-content)
|
|
*/
|
|
open LayoutTypes;
|
|
|
|
open LayoutValue;
|
|
|
|
open LayoutSupport;
|
|
|
|
let gCurrentGenerationCount = ref 0;
|
|
|
|
let gDepth = ref 0;
|
|
|
|
let gPrintTree = {contents: false};
|
|
|
|
let gPrintChanges = {contents: false};
|
|
|
|
let gPrintSkips = {contents: false};
|
|
|
|
let measureString = "measure";
|
|
|
|
let stretchString = "stretch";
|
|
|
|
let absMeasureString = "abs-measure";
|
|
|
|
let absLayoutString = "abs-layout";
|
|
|
|
let initialString = "initial";
|
|
|
|
let flexString = "flex";
|
|
|
|
let spacer = " ";
|
|
|
|
let getSpacer level => {
|
|
let spacerLen = String.length spacer;
|
|
let lvl = level > spacerLen ? level : spacerLen;
|
|
String.sub spacer lvl (String.length spacer)
|
|
};
|
|
|
|
let getModeName (mode, isLayoutInsteadOfMeasure) =>
|
|
switch mode {
|
|
| CSS_MEASURE_MODE_NEGATIVE_ONE_WHATEVER_THAT_MEANS =>
|
|
isLayoutInsteadOfMeasure ?
|
|
"CSS_MEASURE_MODE_NEGATIVE_ONE_WHATEVER_THAT_MEANS" :
|
|
"CSS_MEASURE_MODE_NEGATIVE_ONE_WHATEVER_THAT_MEANS"
|
|
| CssMeasureModeUndefined => isLayoutInsteadOfMeasure ? "LAY_UNDEFINED" : "UNDEFINED"
|
|
| CssMeasureModeExactly => isLayoutInsteadOfMeasure ? "LAY_EXACTLY" : "EXACTLY"
|
|
| CssMeasureModeAtMost => isLayoutInsteadOfMeasure ? "LAY_AT_MOST" : "AT_MOST"
|
|
};
|
|
|
|
let canUseCachedMeasurement
|
|
(
|
|
availableWidth,
|
|
availableHeight,
|
|
marginRow,
|
|
marginColumn,
|
|
widthMeasureMode,
|
|
heightMeasureMode,
|
|
cachedLayout
|
|
) =>
|
|
if (
|
|
cachedLayout.availableWidth == availableWidth &&
|
|
cachedLayout.availableHeight == availableHeight &&
|
|
cachedLayout.widthMeasureMode == widthMeasureMode && cachedLayout.heightMeasureMode == heightMeasureMode
|
|
) {
|
|
true
|
|
} else if
|
|
/* Is it an exact match?*/
|
|
/* If the width is an exact match, try a fuzzy match on the height.*/
|
|
(
|
|
cachedLayout.widthMeasureMode == widthMeasureMode &&
|
|
cachedLayout.availableWidth == availableWidth &&
|
|
heightMeasureMode === CssMeasureModeExactly &&
|
|
availableHeight -. marginColumn == cachedLayout.computedHeight
|
|
) {
|
|
true
|
|
} else if
|
|
/* If the height is an exact match, try a fuzzy match on the width.*/
|
|
(
|
|
cachedLayout.heightMeasureMode == heightMeasureMode &&
|
|
cachedLayout.availableHeight == availableHeight &&
|
|
widthMeasureMode === CssMeasureModeExactly && availableWidth -. marginRow == cachedLayout.computedWidth
|
|
) {
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let cachedMeasurementAt layout i =>
|
|
switch i {
|
|
| 0 => layout.cachedMeasurement1
|
|
| 1 => layout.cachedMeasurement2
|
|
| 2 => layout.cachedMeasurement3
|
|
| 3 => layout.cachedMeasurement4
|
|
| 4 => layout.cachedMeasurement5
|
|
| 5 => layout.cachedMeasurement6
|
|
| _ => raise (Invalid_argument ("No cached measurement at " ^ string_of_int i))
|
|
};
|
|
|
|
|
|
/**
|
|
* 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 above)
|
|
* Return parameter is true if layout was performed, false if skipped
|
|
*/
|
|
let rec layoutNodeInternal
|
|
node
|
|
availableWidth
|
|
availableHeight
|
|
parentDirection
|
|
widthMeasureMode
|
|
heightMeasureMode
|
|
performLayout
|
|
reason => {
|
|
let layout = node.layout;
|
|
gDepth.contents = gDepth.contents + 1;
|
|
let needToVisitNode =
|
|
node.isDirty node.context && layout.generationCount != gCurrentGenerationCount.contents ||
|
|
layout.lastParentDirection != parentDirection;
|
|
if needToVisitNode {
|
|
/* Invalidate the cached results.*/
|
|
layout.nextCachedMeasurementsIndex = 0;
|
|
layout.cachedLayout.widthMeasureMode = CSS_MEASURE_MODE_NEGATIVE_ONE_WHATEVER_THAT_MEANS;
|
|
layout.cachedLayout.heightMeasureMode = CSS_MEASURE_MODE_NEGATIVE_ONE_WHATEVER_THAT_MEANS
|
|
};
|
|
let cachedResults = ref None;
|
|
/* 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 (node.measure !== dummyMeasure && node.childrenCount === 0) {
|
|
let marginAxisRow = getMarginAxis node CssFlexDirectionRow;
|
|
let marginAxisColumn = getMarginAxis node CssFlexDirectionColumn;
|
|
/* First, try to use the layout cache.*/
|
|
if (
|
|
canUseCachedMeasurement (
|
|
availableWidth,
|
|
availableHeight,
|
|
marginAxisRow,
|
|
marginAxisColumn,
|
|
widthMeasureMode,
|
|
heightMeasureMode,
|
|
layout.cachedLayout
|
|
)
|
|
) {
|
|
cachedResults.contents = Some layout.cachedLayout
|
|
} else {
|
|
/* Try to use the measurement cache.*/
|
|
let foundCached = {contents: false};
|
|
for i in 0 to (layout.nextCachedMeasurementsIndex - 1) {
|
|
/* This is basically the "break" */
|
|
if (not foundCached.contents) {
|
|
let cachedMeasurementAtIndex = cachedMeasurementAt layout i;
|
|
if (
|
|
canUseCachedMeasurement (
|
|
availableWidth,
|
|
availableHeight,
|
|
marginAxisRow,
|
|
marginAxisColumn,
|
|
widthMeasureMode,
|
|
heightMeasureMode,
|
|
cachedMeasurementAtIndex
|
|
)
|
|
) {
|
|
cachedResults.contents = Some cachedMeasurementAtIndex;
|
|
foundCached.contents = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if performLayout {
|
|
if (
|
|
layout.cachedLayout.availableWidth == availableWidth &&
|
|
layout.cachedLayout.availableHeight == availableHeight &&
|
|
layout.cachedLayout.widthMeasureMode == widthMeasureMode &&
|
|
layout.cachedLayout.heightMeasureMode == heightMeasureMode
|
|
) {
|
|
cachedResults.contents = Some layout.cachedLayout
|
|
}
|
|
} else {
|
|
let foundCached = {contents: false};
|
|
for i in 0 to (layout.nextCachedMeasurementsIndex - 1) {
|
|
/* This is basically the "break" */
|
|
if (not foundCached.contents) {
|
|
let cachedMeasurementAtIndex = cachedMeasurementAt layout i;
|
|
if (
|
|
cachedMeasurementAtIndex.availableWidth == availableWidth &&
|
|
cachedMeasurementAtIndex.availableHeight == availableHeight &&
|
|
cachedMeasurementAtIndex.widthMeasureMode == widthMeasureMode &&
|
|
cachedMeasurementAtIndex.heightMeasureMode == heightMeasureMode
|
|
) {
|
|
cachedResults.contents = Some cachedMeasurementAtIndex;
|
|
foundCached.contents = true
|
|
}
|
|
}
|
|
}
|
|
};
|
|
if (not needToVisitNode && cachedResults.contents != None) {
|
|
let cachedResults_ =
|
|
switch cachedResults.contents {
|
|
| None => raise (Invalid_argument "Not possible")
|
|
| Some cr => cr
|
|
};
|
|
layout.measuredWidth = cachedResults_.computedWidth;
|
|
layout.measuredHeight = cachedResults_.computedHeight;
|
|
if (gPrintChanges.contents && gPrintSkips.contents) {
|
|
Printf.printf "%s%d.{[skipped] " (getSpacer gDepth.contents) gDepth.contents;
|
|
switch node.print {
|
|
| None => ()
|
|
| Some printer => printer node.context
|
|
};
|
|
Printf.printf
|
|
"wm: %s, hm: %s, aw: %s ah: %s => d: (%s, %s) %s\n"
|
|
(getModeName (widthMeasureMode, performLayout))
|
|
(getModeName (heightMeasureMode, performLayout))
|
|
(scalarToString availableWidth)
|
|
(scalarToString availableHeight)
|
|
(scalarToString cachedResults_.computedWidth)
|
|
(scalarToString cachedResults_.computedHeight)
|
|
reason
|
|
}
|
|
} else {
|
|
if gPrintChanges.contents {
|
|
Printf.printf "%s%d.{%s" (getSpacer gDepth.contents) gDepth.contents (needToVisitNode ? "*" : "");
|
|
switch node.print {
|
|
| None => ()
|
|
| Some printer => printer node.context
|
|
};
|
|
Printf.printf
|
|
"wm: %s, hm: %s, aw: %s ah: %s %s\n"
|
|
(getModeName (widthMeasureMode, performLayout))
|
|
(getModeName (heightMeasureMode, performLayout))
|
|
(scalarToString availableWidth)
|
|
(scalarToString availableHeight)
|
|
reason
|
|
};
|
|
layoutNodeImpl (
|
|
node,
|
|
availableWidth,
|
|
availableHeight,
|
|
parentDirection,
|
|
widthMeasureMode,
|
|
heightMeasureMode,
|
|
performLayout
|
|
);
|
|
if gPrintChanges.contents {
|
|
Printf.printf "%s%d.}%s" (getSpacer gDepth.contents) gDepth.contents (needToVisitNode ? "*" : "");
|
|
switch node.print {
|
|
| None => ()
|
|
| Some printer => printer node.context
|
|
};
|
|
Printf.printf
|
|
"wm: %s, hm: %s, d: (%s, %s) %s\n"
|
|
(getModeName (widthMeasureMode, performLayout))
|
|
(getModeName (heightMeasureMode, performLayout))
|
|
(scalarToString layout.measuredWidth)
|
|
(scalarToString layout.measuredHeight)
|
|
reason
|
|
};
|
|
layout.lastParentDirection = parentDirection;
|
|
if (cachedResults.contents === None) {
|
|
if (layout.nextCachedMeasurementsIndex == css_max_cached_result_count) {
|
|
if gPrintChanges.contents {
|
|
Printf.printf "Out of cache entries!\n"
|
|
};
|
|
layout.nextCachedMeasurementsIndex = 0
|
|
};
|
|
let newCacheEntry =
|
|
performLayout ?
|
|
/* Use the single layout cache entry.*/
|
|
layout.cachedLayout :
|
|
{
|
|
/* Allocate a new measurement cache entry.*/
|
|
let newCacheEntry_ = cachedMeasurementAt layout layout.nextCachedMeasurementsIndex;
|
|
layout.nextCachedMeasurementsIndex = layout.nextCachedMeasurementsIndex + 1;
|
|
newCacheEntry_
|
|
};
|
|
newCacheEntry.availableWidth = availableWidth;
|
|
newCacheEntry.availableHeight = availableHeight;
|
|
newCacheEntry.widthMeasureMode = widthMeasureMode;
|
|
newCacheEntry.heightMeasureMode = heightMeasureMode;
|
|
newCacheEntry.computedWidth = layout.measuredWidth;
|
|
newCacheEntry.computedHeight = layout.measuredHeight
|
|
}
|
|
};
|
|
if performLayout {
|
|
node.layout.width = node.layout.measuredWidth;
|
|
node.layout.height = node.layout.measuredHeight;
|
|
layout.hasNewLayout = true
|
|
};
|
|
gDepth.contents = gDepth.contents - 1;
|
|
layout.generationCount = gCurrentGenerationCount.contents;
|
|
needToVisitNode || cachedResults.contents === None
|
|
}
|
|
and computeChildFlexBasis node child width widthMode height heightMode direction => {
|
|
let mainAxis = resolveAxis node.style.flexDirection direction;
|
|
let isMainAxisRow = isRowDirection mainAxis;
|
|
let childWidth = {contents: zero};
|
|
let childHeight = {contents: zero};
|
|
let childWidthMeasureMode = {contents: CssMeasureModeUndefined};
|
|
let childHeightMeasureMode = {contents: CssMeasureModeUndefined};
|
|
if (isMainAxisRow && isStyleDimDefined child.contents CssFlexDirectionRow) {
|
|
child.contents.layout.computedFlexBasis =
|
|
fmaxf child.contents.style.width (getPaddingAndBorderAxis child.contents CssFlexDirectionRow)
|
|
} else if (
|
|
not isMainAxisRow && isStyleDimDefined child.contents CssFlexDirectionColumn
|
|
) {
|
|
child.contents.layout.computedFlexBasis =
|
|
fmaxf child.contents.style.height (getPaddingAndBorderAxis child.contents CssFlexDirectionColumn)
|
|
} else if (
|
|
not (isUndefined child.contents.style.flexBasis)
|
|
) {
|
|
if (isUndefined child.contents.layout.computedFlexBasis) {
|
|
child.contents.layout.computedFlexBasis =
|
|
fmaxf child.contents.style.flexBasis (getPaddingAndBorderAxis child.contents mainAxis)
|
|
}
|
|
} else {
|
|
childWidth.contents = cssUndefined;
|
|
childHeight.contents = cssUndefined;
|
|
childWidthMeasureMode.contents = CssMeasureModeUndefined;
|
|
childHeightMeasureMode.contents = CssMeasureModeUndefined;
|
|
if (isStyleDimDefined child.contents CssFlexDirectionRow) {
|
|
childWidth.contents = child.contents.style.width +. getMarginAxis child.contents CssFlexDirectionRow;
|
|
childWidthMeasureMode.contents = CssMeasureModeExactly
|
|
};
|
|
|
|
/**
|
|
* Why can't this just be inlined to .height !== cssUndefined.
|
|
*/
|
|
if (isStyleDimDefined child.contents CssFlexDirectionColumn) {
|
|
childHeight.contents =
|
|
child.contents.style.height +. getMarginAxis child.contents CssFlexDirectionColumn;
|
|
childHeightMeasureMode.contents = CssMeasureModeExactly
|
|
};
|
|
if (not isMainAxisRow && node.style.overflow === Scroll || node.style.overflow !== Scroll) {
|
|
if (isUndefined childWidth.contents && not (isUndefined width)) {
|
|
childWidth.contents = width;
|
|
childWidthMeasureMode.contents = CssMeasureModeAtMost
|
|
}
|
|
};
|
|
if (isMainAxisRow && node.style.overflow === Scroll || node.style.overflow !== Scroll) {
|
|
if (isUndefined childHeight.contents && not (isUndefined height)) {
|
|
childHeight.contents = height;
|
|
childHeightMeasureMode.contents = CssMeasureModeAtMost
|
|
}
|
|
};
|
|
/*
|
|
* If child has no defined size in the cross axis and is set to
|
|
* stretch, set the cross axis to be measured exactly with the
|
|
* available inner width.
|
|
*/
|
|
if (
|
|
not isMainAxisRow &&
|
|
not (isUndefined width) &&
|
|
not (isStyleDimDefined child.contents CssFlexDirectionRow) &&
|
|
widthMode === CssMeasureModeExactly && getAlignItem node child.contents === CssAlignStretch
|
|
) {
|
|
childWidth.contents = width;
|
|
childWidthMeasureMode.contents = CssMeasureModeExactly
|
|
};
|
|
if (
|
|
isMainAxisRow &&
|
|
not (isUndefined height) &&
|
|
not (isStyleDimDefined child.contents CssFlexDirectionColumn) &&
|
|
heightMode === CssMeasureModeExactly && getAlignItem node child.contents === CssAlignStretch
|
|
) {
|
|
childHeight.contents = height;
|
|
childHeightMeasureMode.contents = CssMeasureModeExactly
|
|
};
|
|
let _ =
|
|
layoutNodeInternal
|
|
child.contents
|
|
childWidth.contents
|
|
childHeight.contents
|
|
direction
|
|
childWidthMeasureMode.contents
|
|
childHeightMeasureMode.contents
|
|
false
|
|
measureString;
|
|
child.contents.layout.computedFlexBasis =
|
|
fmaxf
|
|
(isMainAxisRow ? child.contents.layout.measuredWidth : child.contents.layout.measuredHeight)
|
|
(getPaddingAndBorderAxis child.contents mainAxis)
|
|
}
|
|
}
|
|
/**
|
|
* By default, mathematical operations are floating point.
|
|
*/
|
|
and layoutNodeImpl
|
|
(
|
|
node,
|
|
availableWidth,
|
|
availableHeight,
|
|
parentDirection,
|
|
widthMeasureMode,
|
|
heightMeasureMode,
|
|
performLayout
|
|
) => {
|
|
|
|
/** START_GENERATED **/
|
|
/* re_assert */
|
|
/* (isUndefined availableWidth ? widthMeasureMode === CssMeasureModeUndefined : true) */
|
|
/* "availableWidth is indefinite so widthMeasureMode must be CssMeasureModeUndefined"; */
|
|
/* re_assert */
|
|
/* (isUndefined availableHeight ? heightMeasureMode === CssMeasureModeUndefined : true) */
|
|
/* "availableHeight is indefinite so heightMeasureMode must be CssMeasureModeUndefined"; */
|
|
let paddingAndBorderAxisRow = getPaddingAndBorderAxis node CssFlexDirectionRow;
|
|
let paddingAndBorderAxisColumn = getPaddingAndBorderAxis node CssFlexDirectionColumn;
|
|
let marginAxisRow = getMarginAxis node CssFlexDirectionRow;
|
|
let marginAxisColumn = getMarginAxis node CssFlexDirectionColumn;
|
|
let direction = resolveDirection node parentDirection;
|
|
node.layout.direction = direction;
|
|
/* For content (text) nodes, determine the dimensions based on the text
|
|
contents. */
|
|
if (node.measure !== dummyMeasure && node.childrenCount === 0) {
|
|
let innerWidth = availableWidth -. marginAxisRow -. paddingAndBorderAxisRow;
|
|
let innerHeight = availableHeight -. marginAxisColumn -. paddingAndBorderAxisColumn;
|
|
if (widthMeasureMode === CssMeasureModeExactly && heightMeasureMode === CssMeasureModeExactly) {
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow (availableWidth -. marginAxisRow);
|
|
node.layout.measuredHeight =
|
|
boundAxis node CssFlexDirectionColumn (availableHeight -. marginAxisColumn)
|
|
} else if (
|
|
not (isUndefined innerWidth) && innerWidth <= zero ||
|
|
not (isUndefined innerHeight) && innerHeight <= zero
|
|
) {
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow zero;
|
|
node.layout.measuredHeight = boundAxis node CssFlexDirectionColumn zero
|
|
} else {
|
|
let measureDim = node.measure node innerWidth widthMeasureMode innerHeight heightMeasureMode;
|
|
node.layout.measuredWidth =
|
|
boundAxis
|
|
node
|
|
CssFlexDirectionRow
|
|
(
|
|
widthMeasureMode === CssMeasureModeUndefined || widthMeasureMode === CssMeasureModeAtMost ?
|
|
measureDim.width +. paddingAndBorderAxisRow : availableWidth -. marginAxisRow
|
|
);
|
|
node.layout.measuredHeight =
|
|
boundAxis
|
|
node
|
|
CssFlexDirectionColumn
|
|
(
|
|
heightMeasureMode === CssMeasureModeUndefined || heightMeasureMode === CssMeasureModeAtMost ?
|
|
measureDim.height +. paddingAndBorderAxisColumn : availableHeight -. marginAxisColumn
|
|
)
|
|
}
|
|
} else {
|
|
let childCount = Array.length node.children;
|
|
if (childCount === 0) {
|
|
node.layout.measuredWidth =
|
|
boundAxis
|
|
node
|
|
CssFlexDirectionRow
|
|
(
|
|
widthMeasureMode === CssMeasureModeUndefined || widthMeasureMode === CssMeasureModeAtMost ?
|
|
paddingAndBorderAxisRow : availableWidth -. marginAxisRow
|
|
);
|
|
node.layout.measuredHeight =
|
|
boundAxis
|
|
node
|
|
CssFlexDirectionColumn
|
|
(
|
|
heightMeasureMode === CssMeasureModeUndefined || heightMeasureMode === CssMeasureModeAtMost ?
|
|
paddingAndBorderAxisColumn : availableHeight -. marginAxisColumn
|
|
)
|
|
} else {
|
|
let shouldContinue = {contents: true};
|
|
if (not performLayout) {
|
|
if (
|
|
(
|
|
(
|
|
widthMeasureMode === CssMeasureModeAtMost &&
|
|
not (isUndefined availableWidth) && availableWidth <= zero
|
|
) &&
|
|
heightMeasureMode === CssMeasureModeAtMost
|
|
) &&
|
|
not (isUndefined availableHeight) && availableHeight <= zero
|
|
) {
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow zero;
|
|
node.layout.measuredHeight = boundAxis node CssFlexDirectionColumn zero;
|
|
shouldContinue.contents = false
|
|
} else if (
|
|
widthMeasureMode === CssMeasureModeAtMost &&
|
|
not (isUndefined availableWidth) && availableWidth <= zero
|
|
) {
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow zero;
|
|
node.layout.measuredHeight =
|
|
boundAxis
|
|
node
|
|
CssFlexDirectionColumn
|
|
(isUndefined availableHeight ? zero : availableHeight -. marginAxisColumn);
|
|
shouldContinue.contents = false
|
|
} else if (
|
|
heightMeasureMode === CssMeasureModeAtMost &&
|
|
not (isUndefined availableHeight) && availableHeight <= zero
|
|
) {
|
|
node.layout.measuredWidth =
|
|
boundAxis
|
|
node CssFlexDirectionRow (isUndefined availableWidth ? zero : availableWidth -. marginAxisRow);
|
|
node.layout.measuredHeight = boundAxis node CssFlexDirectionColumn zero;
|
|
shouldContinue.contents = false
|
|
} else if (
|
|
widthMeasureMode === CssMeasureModeExactly && heightMeasureMode === CssMeasureModeExactly
|
|
) {
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow (availableWidth -. marginAxisRow);
|
|
node.layout.measuredHeight =
|
|
boundAxis node CssFlexDirectionColumn (availableHeight -. marginAxisColumn);
|
|
shouldContinue.contents = false
|
|
}
|
|
};
|
|
if shouldContinue.contents {
|
|
let mainAxis = resolveAxis node.style.flexDirection direction;
|
|
let crossAxis = getCrossFlexDirection mainAxis direction;
|
|
let isMainAxisRow = isRowDirection mainAxis;
|
|
let justifyContent = node.style.justifyContent;
|
|
let isNodeFlexWrap = node.style.flexWrap === CssWrap;
|
|
let firstAbsoluteChild = {contents: theNullNode};
|
|
let currentAbsoluteChild = {contents: theNullNode};
|
|
let leadingPaddingAndBorderMain = getLeadingPaddingAndBorder node mainAxis;
|
|
let trailingPaddingAndBorderMain = getTrailingPaddingAndBorder node mainAxis;
|
|
let leadingPaddingAndBorderCross = getLeadingPaddingAndBorder node crossAxis;
|
|
let paddingAndBorderAxisMain = getPaddingAndBorderAxis node mainAxis;
|
|
let paddingAndBorderAxisCross = getPaddingAndBorderAxis node crossAxis;
|
|
let measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;
|
|
let measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;
|
|
let availableInnerWidth = availableWidth -. marginAxisRow -. paddingAndBorderAxisRow;
|
|
let availableInnerHeight = availableHeight -. marginAxisColumn -. paddingAndBorderAxisColumn;
|
|
let availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;
|
|
let availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;
|
|
let child = {contents: theNullNode};
|
|
/* let i = 0; */
|
|
/* STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM */
|
|
for i in 0 to (childCount - 1) {
|
|
child.contents = node.children.(i);
|
|
if performLayout {
|
|
let childDirection = resolveDirection child.contents direction;
|
|
setPosition child.contents childDirection
|
|
};
|
|
if (child.contents.style.positionType === CssPositionAbsolute) {
|
|
if (firstAbsoluteChild.contents === theNullNode) {
|
|
firstAbsoluteChild.contents = child.contents
|
|
};
|
|
if (currentAbsoluteChild.contents !== theNullNode) {
|
|
currentAbsoluteChild.contents.nextChild = child.contents
|
|
};
|
|
currentAbsoluteChild.contents = child.contents;
|
|
child.contents.nextChild = theNullNode
|
|
} else {
|
|
computeChildFlexBasis
|
|
node
|
|
child
|
|
availableInnerWidth
|
|
widthMeasureMode
|
|
availableInnerHeight
|
|
heightMeasureMode
|
|
direction
|
|
}
|
|
};
|
|
/* STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES */
|
|
let startOfLineIndex = {contents: 0};
|
|
let endOfLineIndex = {contents: 0};
|
|
let lineCount = {contents: 0};
|
|
let totalLineCrossDim = {contents: zero};
|
|
let maxLineMainDim = {contents: zero};
|
|
while (endOfLineIndex.contents < childCount) {
|
|
let itemsOnLine = {contents: 0};
|
|
let sizeConsumedOnCurrentLine = {contents: zero};
|
|
let totalFlexGrowFactors = {contents: zero};
|
|
let totalFlexShrinkScaledFactors = {contents: zero};
|
|
let curIndex = {contents: startOfLineIndex.contents};
|
|
let firstRelativeChild = {contents: theNullNode};
|
|
let currentRelativeChild = {contents: theNullNode};
|
|
let shouldContinue = {contents: true};
|
|
while (curIndex.contents < childCount && shouldContinue.contents) {
|
|
child.contents = node.children.(curIndex.contents);
|
|
child.contents.lineIndex = lineCount.contents;
|
|
if (child.contents.style.positionType !== CssPositionAbsolute) {
|
|
let outerFlexBasis =
|
|
child.contents.layout.computedFlexBasis +. getMarginAxis child.contents mainAxis;
|
|
if (
|
|
(
|
|
sizeConsumedOnCurrentLine.contents +. outerFlexBasis > availableInnerMainDim && isNodeFlexWrap
|
|
) &&
|
|
itemsOnLine.contents > 0
|
|
) {
|
|
shouldContinue.contents = false
|
|
} else {
|
|
sizeConsumedOnCurrentLine.contents = sizeConsumedOnCurrentLine.contents +. outerFlexBasis;
|
|
itemsOnLine.contents = itemsOnLine.contents + 1;
|
|
if (isFlex child.contents) {
|
|
totalFlexGrowFactors.contents =
|
|
totalFlexGrowFactors.contents +. child.contents.style.flexGrow;
|
|
totalFlexShrinkScaledFactors.contents =
|
|
totalFlexShrinkScaledFactors.contents +.
|
|
-. child.contents.style.flexShrink *. child.contents.layout.computedFlexBasis
|
|
};
|
|
if (firstRelativeChild.contents === theNullNode) {
|
|
firstRelativeChild.contents = child.contents
|
|
};
|
|
if (currentRelativeChild.contents !== theNullNode) {
|
|
currentRelativeChild.contents.nextChild = child.contents
|
|
};
|
|
currentRelativeChild.contents = child.contents;
|
|
child.contents.nextChild = theNullNode;
|
|
curIndex.contents = curIndex.contents + 1;
|
|
endOfLineIndex.contents = endOfLineIndex.contents + 1
|
|
}
|
|
} else {
|
|
curIndex.contents = curIndex.contents + 1;
|
|
endOfLineIndex.contents = endOfLineIndex.contents + 1
|
|
}
|
|
};
|
|
let canSkipFlex = not performLayout && measureModeCrossDim === CssMeasureModeExactly;
|
|
let leadingMainDim = {contents: zero};
|
|
let betweenMainDim = {contents: zero};
|
|
let remainingFreeSpace = {contents: zero};
|
|
if (not (isUndefined availableInnerMainDim)) {
|
|
remainingFreeSpace.contents = availableInnerMainDim -. sizeConsumedOnCurrentLine.contents
|
|
} else if (
|
|
sizeConsumedOnCurrentLine.contents < zero
|
|
) {
|
|
remainingFreeSpace.contents = -. sizeConsumedOnCurrentLine.contents
|
|
};
|
|
let originalRemainingFreeSpace = remainingFreeSpace.contents;
|
|
let deltaFreeSpace = {contents: zero};
|
|
if (not canSkipFlex) {
|
|
let childFlexBasis = {contents: zero};
|
|
let flexShrinkScaledFactor = {contents: zero};
|
|
let flexGrowFactor = {contents: zero};
|
|
let baseMainSize = {contents: zero};
|
|
let boundMainSize = {contents: zero};
|
|
let deltaFlexShrinkScaledFactors = {contents: zero};
|
|
let deltaFlexGrowFactors = {contents: zero};
|
|
currentRelativeChild.contents = firstRelativeChild.contents;
|
|
while (currentRelativeChild.contents !== theNullNode) {
|
|
childFlexBasis.contents = currentRelativeChild.contents.layout.computedFlexBasis;
|
|
if (remainingFreeSpace.contents < zero) {
|
|
flexShrinkScaledFactor.contents =
|
|
-. currentRelativeChild.contents.style.flexShrink *. childFlexBasis.contents;
|
|
if (flexShrinkScaledFactor.contents != zero) {
|
|
baseMainSize.contents =
|
|
childFlexBasis.contents +.
|
|
/*
|
|
* Important to first scale, then divide - to support fixed
|
|
* point encoding.
|
|
*/
|
|
flexShrinkScaledFactor.contents *. remainingFreeSpace.contents /.
|
|
totalFlexShrinkScaledFactors.contents;
|
|
boundMainSize.contents =
|
|
boundAxis currentRelativeChild.contents mainAxis baseMainSize.contents;
|
|
if (baseMainSize.contents != boundMainSize.contents) {
|
|
deltaFreeSpace.contents =
|
|
deltaFreeSpace.contents -. (boundMainSize.contents -. childFlexBasis.contents);
|
|
deltaFlexShrinkScaledFactors.contents =
|
|
deltaFlexShrinkScaledFactors.contents -. flexShrinkScaledFactor.contents
|
|
}
|
|
}
|
|
} else if (
|
|
remainingFreeSpace.contents > zero
|
|
) {
|
|
flexGrowFactor.contents = currentRelativeChild.contents.style.flexGrow;
|
|
if (flexGrowFactor.contents != zero) {
|
|
baseMainSize.contents =
|
|
childFlexBasis.contents +.
|
|
/*
|
|
* Important to first scale, then divide - to support fixed
|
|
* point encoding.
|
|
*/
|
|
flexGrowFactor.contents *. remainingFreeSpace.contents /. totalFlexGrowFactors.contents;
|
|
boundMainSize.contents =
|
|
boundAxis currentRelativeChild.contents mainAxis baseMainSize.contents;
|
|
if (baseMainSize.contents != boundMainSize.contents) {
|
|
deltaFreeSpace.contents =
|
|
deltaFreeSpace.contents -. (boundMainSize.contents -. childFlexBasis.contents);
|
|
deltaFlexGrowFactors.contents = deltaFlexGrowFactors.contents -. flexGrowFactor.contents
|
|
}
|
|
}
|
|
};
|
|
currentRelativeChild.contents = currentRelativeChild.contents.nextChild
|
|
};
|
|
totalFlexShrinkScaledFactors.contents =
|
|
totalFlexShrinkScaledFactors.contents +. deltaFlexShrinkScaledFactors.contents;
|
|
totalFlexGrowFactors.contents = totalFlexGrowFactors.contents +. deltaFlexGrowFactors.contents;
|
|
remainingFreeSpace.contents = remainingFreeSpace.contents +. deltaFreeSpace.contents;
|
|
deltaFreeSpace.contents = zero;
|
|
currentRelativeChild.contents = firstRelativeChild.contents;
|
|
while (currentRelativeChild.contents !== theNullNode) {
|
|
childFlexBasis.contents = currentRelativeChild.contents.layout.computedFlexBasis;
|
|
let updatedMainSize = {contents: childFlexBasis.contents};
|
|
if (remainingFreeSpace.contents < zero) {
|
|
flexShrinkScaledFactor.contents =
|
|
-. currentRelativeChild.contents.style.flexShrink *. childFlexBasis.contents;
|
|
if (flexShrinkScaledFactor.contents != zero) {
|
|
updatedMainSize.contents =
|
|
boundAxis
|
|
currentRelativeChild.contents
|
|
mainAxis
|
|
(
|
|
childFlexBasis.contents +.
|
|
/*
|
|
* Important to first scale, then divide - to support
|
|
* fixed point encoding.
|
|
*/
|
|
flexShrinkScaledFactor.contents *. remainingFreeSpace.contents /.
|
|
totalFlexShrinkScaledFactors.contents
|
|
)
|
|
}
|
|
} else if (
|
|
remainingFreeSpace.contents > zero
|
|
) {
|
|
flexGrowFactor.contents = currentRelativeChild.contents.style.flexGrow;
|
|
if (flexGrowFactor.contents != zero) {
|
|
updatedMainSize.contents =
|
|
boundAxis
|
|
currentRelativeChild.contents
|
|
mainAxis
|
|
(
|
|
childFlexBasis.contents +.
|
|
/*
|
|
* Important to first scale, then divide - to support
|
|
* fixed point encoding.
|
|
*/
|
|
flexGrowFactor.contents *. remainingFreeSpace.contents /.
|
|
totalFlexGrowFactors.contents
|
|
)
|
|
}
|
|
};
|
|
deltaFreeSpace.contents =
|
|
deltaFreeSpace.contents -. (updatedMainSize.contents -. childFlexBasis.contents);
|
|
let childWidth = {contents: zero};
|
|
let childHeight = {contents: zero};
|
|
let childWidthMeasureMode = {contents: CssMeasureModeUndefined};
|
|
let childHeightMeasureMode = {contents: CssMeasureModeUndefined};
|
|
if isMainAxisRow {
|
|
childWidth.contents =
|
|
updatedMainSize.contents +.
|
|
getMarginAxis currentRelativeChild.contents CssFlexDirectionRow;
|
|
childWidthMeasureMode.contents = CssMeasureModeExactly;
|
|
if (
|
|
not (isUndefined availableInnerCrossDim) &&
|
|
not (isStyleDimDefined currentRelativeChild.contents CssFlexDirectionColumn) &&
|
|
heightMeasureMode === CssMeasureModeExactly &&
|
|
getAlignItem node currentRelativeChild.contents === CssAlignStretch
|
|
) {
|
|
childHeight.contents = availableInnerCrossDim;
|
|
childHeightMeasureMode.contents = CssMeasureModeExactly
|
|
} else if (
|
|
not (isStyleDimDefined currentRelativeChild.contents CssFlexDirectionColumn)
|
|
) {
|
|
childHeight.contents = availableInnerCrossDim;
|
|
childHeightMeasureMode.contents =
|
|
isUndefined childHeight.contents ? CssMeasureModeUndefined : CssMeasureModeAtMost
|
|
} else {
|
|
childHeight.contents =
|
|
currentRelativeChild.contents.style.height +.
|
|
getMarginAxis currentRelativeChild.contents CssFlexDirectionColumn;
|
|
childHeightMeasureMode.contents = CssMeasureModeExactly
|
|
}
|
|
} else {
|
|
childHeight.contents =
|
|
updatedMainSize.contents +.
|
|
getMarginAxis currentRelativeChild.contents CssFlexDirectionColumn;
|
|
childHeightMeasureMode.contents = CssMeasureModeExactly;
|
|
if (
|
|
not (isUndefined availableInnerCrossDim) &&
|
|
not (isStyleDimDefined currentRelativeChild.contents CssFlexDirectionRow) &&
|
|
widthMeasureMode === CssMeasureModeExactly &&
|
|
getAlignItem node currentRelativeChild.contents === CssAlignStretch
|
|
) {
|
|
childWidth.contents = availableInnerCrossDim;
|
|
childWidthMeasureMode.contents = CssMeasureModeExactly
|
|
} else if (
|
|
not (isStyleDimDefined currentRelativeChild.contents CssFlexDirectionRow)
|
|
) {
|
|
childWidth.contents = availableInnerCrossDim;
|
|
childWidthMeasureMode.contents =
|
|
isUndefined childWidth.contents ? CssMeasureModeUndefined : CssMeasureModeAtMost
|
|
} else {
|
|
childWidth.contents =
|
|
currentRelativeChild.contents.style.width +.
|
|
getMarginAxis currentRelativeChild.contents CssFlexDirectionRow;
|
|
childWidthMeasureMode.contents = CssMeasureModeExactly
|
|
}
|
|
};
|
|
let requiresStretchLayout =
|
|
not (isStyleDimDefined currentRelativeChild.contents crossAxis) &&
|
|
getAlignItem node currentRelativeChild.contents === CssAlignStretch;
|
|
let _ =
|
|
layoutNodeInternal
|
|
currentRelativeChild.contents
|
|
childWidth.contents
|
|
childHeight.contents
|
|
direction
|
|
childWidthMeasureMode.contents
|
|
childHeightMeasureMode.contents
|
|
(performLayout && not requiresStretchLayout)
|
|
flexString;
|
|
currentRelativeChild.contents = currentRelativeChild.contents.nextChild
|
|
}
|
|
};
|
|
remainingFreeSpace.contents = originalRemainingFreeSpace +. deltaFreeSpace.contents;
|
|
/* If we are using "at most" rules in the main axis. Calculate the remaining space when
|
|
constraint by the min size defined for the main axis. */
|
|
if (measureModeMainDim === CssMeasureModeAtMost) {
|
|
let minDim = styleMinDimensionForAxis node mainAxis;
|
|
if (not (isUndefined minDim) && minDim >= 0) {
|
|
remainingFreeSpace.contents =
|
|
fmaxf 0 (minDim - (availableInnerMainDim -. remainingFreeSpace.contents))
|
|
} else {
|
|
remainingFreeSpace.contents = zero
|
|
}
|
|
};
|
|
switch justifyContent {
|
|
| CssJustifyCenter => leadingMainDim.contents = divideScalarByInt remainingFreeSpace.contents 2
|
|
| CssJustifyFlexEnd => leadingMainDim.contents = remainingFreeSpace.contents
|
|
| CssJustifySpaceBetween =>
|
|
if (itemsOnLine.contents > 1) {
|
|
betweenMainDim.contents =
|
|
divideScalarByInt (fmaxf remainingFreeSpace.contents zero) (itemsOnLine.contents - 1)
|
|
} else {
|
|
betweenMainDim.contents = zero
|
|
}
|
|
| CssJustifySpaceAround =>
|
|
betweenMainDim.contents = divideScalarByInt remainingFreeSpace.contents itemsOnLine.contents;
|
|
leadingMainDim.contents = divideScalarByInt betweenMainDim.contents 2
|
|
| CssJustifyFlexStart => ()
|
|
};
|
|
let mainDim = {contents: leadingPaddingAndBorderMain +. leadingMainDim.contents};
|
|
let crossDim = {contents: zero};
|
|
for i in startOfLineIndex.contents to (endOfLineIndex.contents - 1) {
|
|
child.contents = node.children.(i);
|
|
if (
|
|
child.contents.style.positionType === CssPositionAbsolute &&
|
|
isLeadingPosDefinedWithFallback child.contents mainAxis
|
|
) {
|
|
if performLayout {
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
mainAxis
|
|
(
|
|
getLeadingPositionWithFallback child.contents mainAxis +. getLeadingBorder node mainAxis +.
|
|
getLeadingMargin child.contents mainAxis
|
|
)
|
|
}
|
|
} else {
|
|
if performLayout {
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
mainAxis
|
|
(layoutPosPositionForAxis child.contents mainAxis +. mainDim.contents)
|
|
};
|
|
if (child.contents.style.positionType === CssPositionRelative) {
|
|
if canSkipFlex {
|
|
mainDim.contents =
|
|
mainDim.contents +. betweenMainDim.contents +. getMarginAxis child.contents mainAxis +.
|
|
child.contents.layout.computedFlexBasis;
|
|
crossDim.contents = availableInnerCrossDim
|
|
} else {
|
|
mainDim.contents =
|
|
mainDim.contents +. betweenMainDim.contents +. getDimWithMargin child.contents mainAxis;
|
|
crossDim.contents = fmaxf crossDim.contents (getDimWithMargin child.contents crossAxis)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
mainDim.contents = mainDim.contents +. trailingPaddingAndBorderMain;
|
|
let containerCrossAxis = {contents: availableInnerCrossDim};
|
|
if (
|
|
measureModeCrossDim === CssMeasureModeUndefined || measureModeCrossDim === CssMeasureModeAtMost
|
|
) {
|
|
containerCrossAxis.contents =
|
|
boundAxis node crossAxis (crossDim.contents +. paddingAndBorderAxisCross) -. paddingAndBorderAxisCross;
|
|
if (measureModeCrossDim === CssMeasureModeAtMost) {
|
|
containerCrossAxis.contents = fminf containerCrossAxis.contents availableInnerCrossDim
|
|
}
|
|
};
|
|
if (not isNodeFlexWrap && measureModeCrossDim === CssMeasureModeExactly) {
|
|
crossDim.contents = availableInnerCrossDim
|
|
};
|
|
crossDim.contents =
|
|
boundAxis node crossAxis (crossDim.contents +. paddingAndBorderAxisCross) -. paddingAndBorderAxisCross;
|
|
/*
|
|
* STEP 7: CROSS-AXIS ALIGNMENT We can skip child alignment if we're
|
|
* just measuring the container.
|
|
*/
|
|
if performLayout {
|
|
for i in startOfLineIndex.contents to (endOfLineIndex.contents - 1) {
|
|
child.contents = node.children.(i);
|
|
if (child.contents.style.positionType === CssPositionAbsolute) {
|
|
if (isLeadingPosDefinedWithFallback child.contents crossAxis) {
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(
|
|
getLeadingPositionWithFallback child.contents crossAxis +.
|
|
getLeadingBorder node crossAxis +.
|
|
getLeadingMargin child.contents crossAxis
|
|
)
|
|
} else {
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(leadingPaddingAndBorderCross +. getLeadingMargin child.contents crossAxis)
|
|
}
|
|
} else {
|
|
let leadingCrossDim = {contents: leadingPaddingAndBorderCross};
|
|
let alignItem = getAlignItem node child.contents;
|
|
if (alignItem === CssAlignStretch) {
|
|
let childWidth = {contents: zero};
|
|
let childHeight = {contents: zero};
|
|
let childWidthMeasureMode = {contents: CssMeasureModeUndefined};
|
|
let childHeightMeasureMode = {contents: CssMeasureModeUndefined};
|
|
childWidth.contents =
|
|
child.contents.layout.measuredWidth +. getMarginAxis child.contents CssFlexDirectionRow;
|
|
childHeight.contents =
|
|
child.contents.layout.measuredHeight +.
|
|
getMarginAxis child.contents CssFlexDirectionColumn;
|
|
let isCrossSizeDefinite = {contents: false};
|
|
if isMainAxisRow {
|
|
isCrossSizeDefinite.contents = isStyleDimDefined child.contents CssFlexDirectionColumn;
|
|
childHeight.contents = crossDim.contents
|
|
} else {
|
|
isCrossSizeDefinite.contents = isStyleDimDefined child.contents CssFlexDirectionRow;
|
|
childWidth.contents = crossDim.contents
|
|
};
|
|
if (not isCrossSizeDefinite.contents) {
|
|
childWidthMeasureMode.contents =
|
|
isUndefined childWidth.contents ? CssMeasureModeUndefined : CssMeasureModeExactly;
|
|
childHeightMeasureMode.contents =
|
|
isUndefined childHeight.contents ? CssMeasureModeUndefined : CssMeasureModeExactly;
|
|
let _ =
|
|
layoutNodeInternal
|
|
child.contents
|
|
childWidth.contents
|
|
childHeight.contents
|
|
direction
|
|
childWidthMeasureMode.contents
|
|
childHeightMeasureMode.contents
|
|
true
|
|
stretchString;
|
|
()
|
|
}
|
|
} else if (
|
|
alignItem !== CssAlignFlexStart
|
|
) {
|
|
let remainingCrossDim =
|
|
containerCrossAxis.contents -. getDimWithMargin child.contents crossAxis;
|
|
if (alignItem === CssAlignCenter) {
|
|
leadingCrossDim.contents =
|
|
leadingCrossDim.contents +. divideScalarByInt remainingCrossDim 2
|
|
} else {
|
|
leadingCrossDim.contents = leadingCrossDim.contents +. remainingCrossDim
|
|
}
|
|
};
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(
|
|
layoutPosPositionForAxis child.contents crossAxis +. totalLineCrossDim.contents +.
|
|
leadingCrossDim.contents
|
|
)
|
|
}
|
|
}
|
|
};
|
|
totalLineCrossDim.contents = totalLineCrossDim.contents +. crossDim.contents;
|
|
maxLineMainDim.contents = fmaxf maxLineMainDim.contents mainDim.contents;
|
|
lineCount.contents = lineCount.contents + 1;
|
|
startOfLineIndex.contents = endOfLineIndex.contents
|
|
};
|
|
if (lineCount.contents > 1 && performLayout && not (isUndefined availableInnerCrossDim)) {
|
|
let remainingAlignContentDim = availableInnerCrossDim -. totalLineCrossDim.contents;
|
|
let crossDimLead = {contents: zero};
|
|
let currentLead = {contents: leadingPaddingAndBorderCross};
|
|
let alignContent = node.style.alignContent;
|
|
if (alignContent === CssAlignFlexEnd) {
|
|
currentLead.contents = currentLead.contents +. remainingAlignContentDim
|
|
} else if (
|
|
alignContent === CssAlignCenter
|
|
) {
|
|
currentLead.contents = currentLead.contents +. divideScalarByInt remainingAlignContentDim 2
|
|
} else if (
|
|
alignContent === CssAlignStretch
|
|
) {
|
|
if (availableInnerCrossDim > totalLineCrossDim.contents) {
|
|
crossDimLead.contents = divideScalarByInt remainingAlignContentDim lineCount.contents
|
|
}
|
|
};
|
|
let endIndex = {contents: 0};
|
|
for i in 0 to (lineCount.contents - 1) {
|
|
let startIndex = endIndex.contents;
|
|
let j = {contents: startIndex};
|
|
let lineHeight = {contents: zero};
|
|
let shouldContinue = {contents: false};
|
|
while (j.contents < childCount && shouldContinue.contents) {
|
|
child.contents = node.children.(j.contents);
|
|
if (child.contents.style.positionType === CssPositionRelative) {
|
|
if (child.contents.lineIndex !== i) {
|
|
shouldContinue.contents = false
|
|
} else if (
|
|
isLayoutDimDefined child.contents crossAxis
|
|
) {
|
|
lineHeight.contents =
|
|
fmaxf
|
|
lineHeight.contents
|
|
(
|
|
layoutMeasuredDimensionForAxis child.contents crossAxis +.
|
|
getMarginAxis child.contents crossAxis
|
|
)
|
|
}
|
|
};
|
|
j.contents = j.contents + 1
|
|
};
|
|
endIndex.contents = j.contents;
|
|
lineHeight.contents = lineHeight.contents +. crossDimLead.contents;
|
|
if performLayout {
|
|
for j in startIndex to (endIndex.contents - 1) {
|
|
child.contents = node.children.(j);
|
|
if (child.contents.style.positionType === CssPositionRelative) {
|
|
switch (getAlignItem node child.contents) {
|
|
| CssAlignFlexStart =>
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(currentLead.contents +. getLeadingMargin child.contents crossAxis)
|
|
| CssAlignFlexEnd =>
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(
|
|
currentLead.contents +. lineHeight.contents -.
|
|
getTrailingMargin child.contents crossAxis -.
|
|
layoutMeasuredDimensionForAxis child.contents crossAxis
|
|
)
|
|
| CssAlignCenter =>
|
|
let childHeight = layoutMeasuredDimensionForAxis child.contents crossAxis;
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(currentLead.contents +. divideScalarByInt (lineHeight.contents -. childHeight) 2)
|
|
| CssAlignStretch =>
|
|
setLayoutLeadingPositionForAxis
|
|
child.contents
|
|
crossAxis
|
|
(currentLead.contents +. getLeadingMargin child.contents crossAxis)
|
|
| CssAlignAuto => raise (Invalid_argument "getAlignItem should never return auto")
|
|
}
|
|
}
|
|
}
|
|
};
|
|
currentLead.contents = currentLead.contents +. lineHeight.contents
|
|
}
|
|
};
|
|
/* STEP 9: COMPUTING FINAL DIMENSIONS */
|
|
node.layout.measuredWidth = boundAxis node CssFlexDirectionRow (availableWidth -. marginAxisRow);
|
|
node.layout.measuredHeight =
|
|
boundAxis node CssFlexDirectionColumn (availableHeight -. marginAxisColumn);
|
|
/* If the user didn't specify a width or height for the node, set the
|
|
* dimensions based on the children. */
|
|
if (measureModeMainDim === CssMeasureModeUndefined) {
|
|
setLayoutMeasuredDimensionForAxis node mainAxis (boundAxis node mainAxis maxLineMainDim.contents)
|
|
} else if (
|
|
measureModeMainDim === CssMeasureModeAtMost
|
|
) {
|
|
setLayoutMeasuredDimensionForAxis
|
|
node
|
|
mainAxis
|
|
(
|
|
fmaxf
|
|
(
|
|
fminf
|
|
(availableInnerMainDim +. paddingAndBorderAxisMain)
|
|
(boundAxisWithinMinAndMax node mainAxis maxLineMainDim.contents)
|
|
)
|
|
paddingAndBorderAxisMain
|
|
)
|
|
};
|
|
if (measureModeCrossDim === CssMeasureModeUndefined) {
|
|
setLayoutMeasuredDimensionForAxis
|
|
node
|
|
crossAxis
|
|
(boundAxis node crossAxis (totalLineCrossDim.contents +. paddingAndBorderAxisCross))
|
|
} else if (
|
|
measureModeCrossDim === CssMeasureModeAtMost
|
|
) {
|
|
setLayoutMeasuredDimensionForAxis
|
|
node
|
|
crossAxis
|
|
(
|
|
fmaxf
|
|
(
|
|
fminf
|
|
(availableInnerCrossDim +. paddingAndBorderAxisCross)
|
|
(
|
|
boundAxisWithinMinAndMax
|
|
node crossAxis (totalLineCrossDim.contents +. paddingAndBorderAxisCross)
|
|
)
|
|
)
|
|
paddingAndBorderAxisCross
|
|
)
|
|
};
|
|
currentAbsoluteChild.contents = firstAbsoluteChild.contents;
|
|
while (currentAbsoluteChild.contents !== theNullNode) {
|
|
if performLayout {
|
|
let childWidth = {contents: cssUndefined};
|
|
let childHeight = {contents: cssUndefined};
|
|
let childWidthMeasureMode = {contents: CssMeasureModeUndefined};
|
|
let childHeightMeasureMode = {contents: CssMeasureModeUndefined};
|
|
if (isStyleDimDefined currentAbsoluteChild.contents CssFlexDirectionRow) {
|
|
childWidth.contents =
|
|
currentAbsoluteChild.contents.style.width +.
|
|
getMarginAxis currentAbsoluteChild.contents CssFlexDirectionRow
|
|
} else if (
|
|
isLeadingPosDefinedWithFallback currentAbsoluteChild.contents CssFlexDirectionRow &&
|
|
isTrailingPosDefinedWithFallback currentAbsoluteChild.contents CssFlexDirectionRow
|
|
) {
|
|
childWidth.contents =
|
|
node.layout.measuredWidth -. (
|
|
getLeadingBorder node CssFlexDirectionRow +. getTrailingBorder node CssFlexDirectionRow
|
|
) -. (
|
|
getLeadingPositionWithFallback currentAbsoluteChild.contents CssFlexDirectionRow +.
|
|
getTrailingPositionWithFallback currentAbsoluteChild.contents CssFlexDirectionRow
|
|
);
|
|
childWidth.contents =
|
|
boundAxis currentAbsoluteChild.contents CssFlexDirectionRow childWidth.contents
|
|
};
|
|
if (isStyleDimDefined currentAbsoluteChild.contents CssFlexDirectionColumn) {
|
|
childHeight.contents =
|
|
currentAbsoluteChild.contents.style.height +.
|
|
getMarginAxis currentAbsoluteChild.contents CssFlexDirectionColumn
|
|
} else if (
|
|
/* If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. */
|
|
isLeadingPosDefinedWithFallback currentAbsoluteChild.contents CssFlexDirectionColumn &&
|
|
isTrailingPosDefinedWithFallback currentAbsoluteChild.contents CssFlexDirectionColumn
|
|
) {
|
|
childHeight.contents =
|
|
node.layout.measuredHeight -. (
|
|
getLeadingBorder node CssFlexDirectionColumn +.
|
|
getTrailingBorder node CssFlexDirectionColumn
|
|
) -. (
|
|
getLeadingPositionWithFallback currentAbsoluteChild.contents CssFlexDirectionColumn +.
|
|
getTrailingPositionWithFallback currentAbsoluteChild.contents CssFlexDirectionColumn
|
|
);
|
|
childHeight.contents =
|
|
boundAxis currentAbsoluteChild.contents CssFlexDirectionColumn childHeight.contents
|
|
};
|
|
if (isUndefined childWidth.contents || isUndefined childHeight.contents) {
|
|
childWidthMeasureMode.contents =
|
|
isUndefined childWidth.contents ? CssMeasureModeUndefined : CssMeasureModeExactly;
|
|
childHeightMeasureMode.contents =
|
|
isUndefined childHeight.contents ? CssMeasureModeUndefined : CssMeasureModeExactly;
|
|
/*
|
|
* 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 (
|
|
(not isMainAxisRow && isUndefined childWidth.contents) &&
|
|
not (isUndefined availableInnerWidth)
|
|
) {
|
|
childWidth.contents = availableInnerWidth;
|
|
childWidthMeasureMode.contents = CssMeasureModeAtMost
|
|
};
|
|
/*
|
|
* If child has no defined size in the cross axis and is set to stretch, set the cross
|
|
* axis to be measured exactly with the available inner width
|
|
*/
|
|
let _ =
|
|
layoutNodeInternal
|
|
currentAbsoluteChild.contents
|
|
childWidth.contents
|
|
childHeight.contents
|
|
direction
|
|
childWidthMeasureMode.contents
|
|
childHeightMeasureMode.contents
|
|
false
|
|
absMeasureString;
|
|
childWidth.contents =
|
|
currentAbsoluteChild.contents.layout.measuredWidth +.
|
|
getMarginAxis currentAbsoluteChild.contents CssFlexDirectionRow;
|
|
childHeight.contents =
|
|
currentAbsoluteChild.contents.layout.measuredHeight +.
|
|
getMarginAxis currentAbsoluteChild.contents CssFlexDirectionColumn
|
|
};
|
|
let _ =
|
|
layoutNodeInternal
|
|
currentAbsoluteChild.contents
|
|
childWidth.contents
|
|
childHeight.contents
|
|
direction
|
|
CssMeasureModeExactly
|
|
CssMeasureModeExactly
|
|
true
|
|
absLayoutString;
|
|
if (
|
|
isTrailingPosDefinedWithFallback currentAbsoluteChild.contents mainAxis &&
|
|
not (isLeadingPosDefinedWithFallback currentAbsoluteChild.contents mainAxis)
|
|
) {
|
|
setLayoutLeadingPositionForAxis
|
|
currentAbsoluteChild.contents
|
|
mainAxis
|
|
(
|
|
layoutMeasuredDimensionForAxis node mainAxis -.
|
|
layoutMeasuredDimensionForAxis currentAbsoluteChild.contents mainAxis -.
|
|
getTrailingPositionWithFallback currentAbsoluteChild.contents mainAxis
|
|
)
|
|
};
|
|
if (
|
|
isTrailingPosDefinedWithFallback currentAbsoluteChild.contents crossAxis &&
|
|
not (isLeadingPosDefinedWithFallback currentAbsoluteChild.contents crossAxis)
|
|
) {
|
|
setLayoutLeadingPositionForAxis
|
|
currentAbsoluteChild.contents
|
|
crossAxis
|
|
(
|
|
layoutMeasuredDimensionForAxis node crossAxis -.
|
|
layoutMeasuredDimensionForAxis currentAbsoluteChild.contents crossAxis -.
|
|
getTrailingPositionWithFallback currentAbsoluteChild.contents crossAxis
|
|
)
|
|
}
|
|
};
|
|
currentAbsoluteChild.contents = currentAbsoluteChild.contents.nextChild
|
|
};
|
|
/* STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN */
|
|
if performLayout {
|
|
let needsMainTrailingPos =
|
|
mainAxis == CssFlexDirectionRowReverse || mainAxis == CssFlexDirectionColumnReverse;
|
|
let needsCrossTrailingPos =
|
|
crossAxis == CssFlexDirectionRowReverse || crossAxis == CssFlexDirectionColumnReverse;
|
|
/* Set trailing position if necessary. */
|
|
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
|
for i in 0 to (childCount - 1) {
|
|
let child = node.children.(i);
|
|
if needsMainTrailingPos {
|
|
setTrailingPosition node child mainAxis
|
|
};
|
|
if needsCrossTrailingPos {
|
|
setTrailingPosition node child crossAxis
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/** END_GENERATED **/
|
|
};
|
|
|
|
let layoutNode node availableWidth availableHeight 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.*/
|
|
gCurrentGenerationCount.contents = gCurrentGenerationCount.contents + 1;
|
|
/* If the caller didn't specify a height/width, use the dimensions*/
|
|
/* specified in the style.*/
|
|
let (availableWidth, widthMeasureMode) =
|
|
if (not (isUndefined availableWidth)) {
|
|
(availableWidth, CssMeasureModeExactly)
|
|
} else if (
|
|
isStyleDimDefined node CssFlexDirectionRow
|
|
) {
|
|
(node.style.width +. getMarginAxis node CssFlexDirectionRow, CssMeasureModeExactly)
|
|
} else if (
|
|
node.style.maxWidth >= zero
|
|
) {
|
|
(node.style.maxWidth, CssMeasureModeAtMost)
|
|
} else {
|
|
(availableWidth, CssMeasureModeUndefined)
|
|
};
|
|
let (availableHeight, heightMeasureMode) =
|
|
if (not (isUndefined availableHeight)) {
|
|
(availableHeight, CssMeasureModeExactly)
|
|
} else if (
|
|
isStyleDimDefined node CssFlexDirectionColumn
|
|
) {
|
|
(node.style.height +. getMarginAxis node CssFlexDirectionColumn, CssMeasureModeExactly)
|
|
} else if (
|
|
node.style.maxHeight >= zero
|
|
) {
|
|
(node.style.maxHeight, CssMeasureModeAtMost)
|
|
} else {
|
|
(availableHeight, CssMeasureModeUndefined)
|
|
};
|
|
if (
|
|
layoutNodeInternal
|
|
node
|
|
availableWidth
|
|
availableHeight
|
|
parentDirection
|
|
widthMeasureMode
|
|
heightMeasureMode
|
|
true
|
|
initialString
|
|
) {
|
|
setPosition node node.layout.direction;
|
|
if gPrintTree.contents {
|
|
LayoutPrint.printCssNode (node, {printLayout: true, printChildren: true, printStyle: true})
|
|
}
|
|
}
|
|
}; |