Alter layout engine to conform closer to W3C spec

The primary goals of this change are:
  - Better conformance to the W3C flexbox standard (https://www.w3.org/TR/css-flexbox-1/)
    and a clear articulation of the areas where it deviates from the spec.
  - Support for flex-shrink.
  - Conformance with layout effects of "overflow: hidden".

Specifically, here are the limitations of this implementation as compared to the W3C
flexbox standard (this is also documented in Layout.js):
  - 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 0 0
      - 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).

And here is how the implementation deviates from the standard (this is also documented in
Layout.js):
  - 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'.
This commit is contained in:
Adam Comella
2016-04-26 16:35:46 -07:00
parent b0d00ad338
commit f3dd51ab97
36 changed files with 9789 additions and 4157 deletions

View File

@@ -0,0 +1,19 @@
/**
* 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.
*/
package com.facebook.csslayout;
public class CSSCachedMeasurement {
public float availableWidth;
public float availableHeight;
public CSSMeasureMode widthMeasureMode = null;
public CSSMeasureMode heightMeasureMode = null;
public float computedWidth;
public float computedHeight;
}

View File

@@ -14,6 +14,10 @@ import java.util.Arrays;
* Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode.
*/
public class CSSLayout {
// This value was chosen based on empiracle data. Even the most complicated
// layouts should not require more than 16 entries to fit within the cache.
public static final int MAX_CACHED_RESULT_COUNT = 16;
public static final int POSITION_LEFT = 0;
public static final int POSITION_TOP = 1;
public static final int POSITION_RIGHT = 2;
@@ -25,24 +29,38 @@ public class CSSLayout {
public float[] position = new float[4];
public float[] dimensions = new float[2];
public CSSDirection direction = CSSDirection.LTR;
/**
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
*/
public float flexBasis;
public int generationCount;
public CSSDirection lastParentDirection;
public int nextCachedMeasurementsIndex;
public CSSCachedMeasurement[] cachedMeasurements = new CSSCachedMeasurement[MAX_CACHED_RESULT_COUNT];
public float[] measuredDimensions = new float[2];
public CSSCachedMeasurement cachedLayout = new CSSCachedMeasurement();
CSSLayout() {
resetResult();
}
public void resetResult() {
Arrays.fill(position, 0);
Arrays.fill(dimensions, CSSConstants.UNDEFINED);
direction = CSSDirection.LTR;
}
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;
flexBasis = 0;
generationCount = 0;
lastParentDirection = null;
nextCachedMeasurementsIndex = 0;
measuredDimensions[DIMENSION_WIDTH] = CSSConstants.UNDEFINED;
measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.UNDEFINED;
cachedLayout.widthMeasureMode = null;
cachedLayout.heightMeasureMode = null;
}
@Override

View File

@@ -17,4 +17,5 @@ package com.facebook.csslayout;
*/
public class CSSLayoutContext {
/*package*/ final MeasureOutput measureOutput = new MeasureOutput();
int currentGenerationCount;
}

View File

@@ -63,9 +63,8 @@ public class CSSNode {
public int lineIndex = 0;
/*package*/ CSSNode nextAbsoluteChild;
/*package*/ CSSNode nextFlexChild;
/*package*/ CSSNode nextChild;
private @Nullable ArrayList<CSSNode> mChildren;
private @Nullable CSSNode mParent;
private @Nullable MeasureFunction mMeasureFunction = null;
@@ -139,7 +138,6 @@ public class CSSNode {
* Performs the actual layout and saves the results in {@link #layout}
*/
public void calculateLayout(CSSLayoutContext layoutContext) {
layout.resetResult();
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, CSSConstants.UNDEFINED, null);
}

View File

@@ -0,0 +1,14 @@
/**
* 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.
*/
package com.facebook.csslayout;
public enum CSSOverflow {
VISIBLE,
HIDDEN,
}

View File

@@ -23,6 +23,7 @@ public class CSSStyle {
public CSSAlign alignSelf;
public CSSPositionType positionType;
public CSSWrap flexWrap;
public CSSOverflow overflow;
public float flex;
public Spacing margin = new Spacing();
@@ -51,6 +52,7 @@ public class CSSStyle {
alignSelf = CSSAlign.AUTO;
positionType = CSSPositionType.RELATIVE;
flexWrap = CSSWrap.NOWRAP;
overflow = CSSOverflow.VISIBLE;
flex = 0f;
margin.reset();;

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@ public class LayoutCachingTest {
root.addChildAt(c0, 0);
root.addChildAt(c1, 1);
c0.addChildAt(c0c0, 0);
root.calculateLayout(layoutContext);
assertTreeHasNewLayout(true, root);
markLayoutAppliedForTree(root);

File diff suppressed because it is too large Load Diff