Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Pierre Renaux
2015-05-17 21:54:30 +08:00
17 changed files with 8118 additions and 1334 deletions

View File

@@ -0,0 +1,15 @@
/**
* 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 CSSDirection {
INHERIT,
LTR,
RTL,
}

View File

@@ -10,5 +10,7 @@ package com.facebook.csslayout;
public enum CSSFlexDirection {
COLUMN,
COLUMN_REVERSE,
ROW,
ROW_REVERSE
}

View File

@@ -13,8 +13,10 @@ package com.facebook.csslayout;
*/
public class CSSLayout {
public float x;
public float y;
public float top;
public float left;
public float right;
public float bottom;
public float width = CSSConstants.UNDEFINED;
public float height = CSSConstants.UNDEFINED;
@@ -22,15 +24,19 @@ public class CSSLayout {
* This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)}
*/
public void resetResult() {
x = 0;
y = 0;
left = 0;
top = 0;
right = 0;
bottom = 0;
width = CSSConstants.UNDEFINED;
height = CSSConstants.UNDEFINED;
}
public void copy(CSSLayout layout) {
x = layout.x;
y = layout.y;
left = layout.left;
top = layout.top;
right = layout.right;
bottom = layout.bottom;
width = layout.width;
height = layout.height;
}
@@ -38,8 +44,8 @@ public class CSSLayout {
@Override
public String toString() {
return "layout: {" +
"x: " + x + ", " +
"y: " + y + ", " +
"left: " + left + ", " +
"top: " + top + ", " +
"width: " + width + ", " +
"height: " + height +
"}";

View File

@@ -49,10 +49,6 @@ public class CSSNode {
public void measure(CSSNode node, float width, MeasureOutput measureOutput);
}
private final float[] mMargin = Spacing.newFullSpacingArray();
private final float[] mPadding = Spacing.newFullSpacingArray();
private final float[] mBorder = Spacing.newFullSpacingArray();
// VisibleForTesting
/*package*/ final CSSStyle style = new CSSStyle();
/*package*/ final CSSLayout layout = new CSSLayout();
@@ -127,7 +123,7 @@ public class CSSNode {
*/
public void calculateLayout(CSSLayoutContext layoutContext) {
layout.resetResult();
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED);
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, null);
}
/**
@@ -265,24 +261,19 @@ public class CSSNode {
}
public void setMargin(int spacingType, float margin) {
setSpacing(mMargin, style.margin, spacingType, margin);
if (style.margin.set(spacingType, margin)) {
dirty();
}
}
public void setPadding(int spacingType, float padding) {
setSpacing(mPadding, style.padding, spacingType, padding);
if (style.padding.set(spacingType, padding)) {
dirty();
}
}
public void setBorder(int spacingType, float border) {
setSpacing(mBorder, style.border, spacingType, border);
}
protected void setSpacing(
float[] spacingDef,
float[] cssStyle,
int spacingType,
float spacing) {
if (!valuesEqual(spacingDef[spacingType], spacing)) {
Spacing.updateSpacing(spacingDef, cssStyle, spacingType, spacing, 0);
if (style.border.set(spacingType, border)) {
dirty();
}
}
@@ -330,11 +321,11 @@ public class CSSNode {
}
public float getLayoutX() {
return layout.x;
return layout.left;
}
public float getLayoutY() {
return layout.y;
return layout.top;
}
public float getLayoutWidth() {
@@ -344,4 +335,34 @@ public class CSSNode {
public float getLayoutHeight() {
return layout.height;
}
/**
* Get this node's padding, as defined by style + default padding.
*/
public Spacing getStylePadding() {
return style.padding;
}
/**
* Get this node's width, as defined in the style.
*/
public float getStyleWidth() {
return style.width;
}
/**
* Get this node's height, as defined in the style.
*/
public float getStyleHeight() {
return style.height;
}
/**
* Set a default padding (left/top/right/bottom) for this node.
*/
public void setDefaultPadding(int spacingType, float padding) {
if (style.padding.setDefault(spacingType, padding)) {
dirty();
}
}
}

View File

@@ -13,6 +13,7 @@ package com.facebook.csslayout;
*/
public class CSSStyle {
public CSSDirection direction = CSSDirection.INHERIT;
public CSSFlexDirection flexDirection = CSSFlexDirection.COLUMN;
public CSSJustify justifyContent = CSSJustify.FLEX_START;
public CSSAlign alignContent = CSSAlign.FLEX_START;
@@ -22,9 +23,9 @@ public class CSSStyle {
public CSSWrap flexWrap = CSSWrap.NOWRAP;
public float flex;
public float[] margin = Spacing.newSpacingResultArray();
public float[] padding = Spacing.newSpacingResultArray();
public float[] border = Spacing.newSpacingResultArray();
public Spacing margin = new Spacing();
public Spacing padding = new Spacing();
public Spacing border = new Spacing();
public float positionTop = CSSConstants.UNDEFINED;
public float positionBottom = CSSConstants.UNDEFINED;

View File

@@ -18,6 +18,8 @@ public class LayoutEngine {
LEFT,
BOTTOM,
RIGHT,
START,
END,
}
private static enum DimensionIndex {
@@ -28,24 +30,34 @@ public class LayoutEngine {
private static void setLayoutPosition(CSSNode node, PositionIndex position, float value) {
switch (position) {
case TOP:
node.layout.y = value;
node.layout.top = value;
break;
case LEFT:
node.layout.x = value;
node.layout.left = value;
break;
case RIGHT:
node.layout.right = value;
break;
case BOTTOM:
node.layout.bottom = value;
break;
default:
throw new RuntimeException("Didn't get TOP or LEFT!");
throw new RuntimeException("Didn't get TOP, LEFT, RIGHT, or BOTTOM!");
}
}
private static float getLayoutPosition(CSSNode node, PositionIndex position) {
switch (position) {
case TOP:
return node.layout.y;
return node.layout.top;
case LEFT:
return node.layout.x;
return node.layout.left;
case RIGHT:
return node.layout.right;
case BOTTOM:
return node.layout.bottom;
default:
throw new RuntimeException("Didn't get TOP or LEFT!");
throw new RuntimeException("Didn't get TOP, LEFT, RIGHT, or BOTTOM!");
}
}
@@ -100,19 +112,61 @@ public class LayoutEngine {
}
private static PositionIndex getLeading(CSSFlexDirection axis) {
return axis == CSSFlexDirection.COLUMN ? PositionIndex.TOP : PositionIndex.LEFT;
switch (axis) {
case COLUMN:
return PositionIndex.TOP;
case COLUMN_REVERSE:
return PositionIndex.BOTTOM;
case ROW:
return PositionIndex.LEFT;
case ROW_REVERSE:
return PositionIndex.RIGHT;
default:
throw new RuntimeException("Didn't get TOP, LEFT, RIGHT, or BOTTOM!");
}
}
private static PositionIndex getTrailing(CSSFlexDirection axis) {
return axis == CSSFlexDirection.COLUMN ? PositionIndex.BOTTOM : PositionIndex.RIGHT;
switch (axis) {
case COLUMN:
return PositionIndex.BOTTOM;
case COLUMN_REVERSE:
return PositionIndex.TOP;
case ROW:
return PositionIndex.RIGHT;
case ROW_REVERSE:
return PositionIndex.LEFT;
default:
throw new RuntimeException("Didn't get COLUMN, COLUMN_REVERSE, ROW, or ROW_REVERSE!");
}
}
private static PositionIndex getPos(CSSFlexDirection axis) {
return axis == CSSFlexDirection.COLUMN ? PositionIndex.TOP : PositionIndex.LEFT;
switch (axis) {
case COLUMN:
return PositionIndex.TOP;
case COLUMN_REVERSE:
return PositionIndex.BOTTOM;
case ROW:
return PositionIndex.LEFT;
case ROW_REVERSE:
return PositionIndex.RIGHT;
default:
throw new RuntimeException("Didn't get COLUMN, COLUMN_REVERSE, ROW, or ROW_REVERSE!");
}
}
private static DimensionIndex getDim(CSSFlexDirection axis) {
return axis == CSSFlexDirection.COLUMN ? DimensionIndex.HEIGHT : DimensionIndex.WIDTH;
switch (axis) {
case COLUMN:
case COLUMN_REVERSE:
return DimensionIndex.HEIGHT;
case ROW:
case ROW_REVERSE:
return DimensionIndex.WIDTH;
default:
throw new RuntimeException("Didn't get COLUMN, COLUMN_REVERSE, ROW, or ROW_REVERSE!");
}
}
private static boolean isDimDefined(CSSNode node, CSSFlexDirection axis) {
@@ -132,74 +186,154 @@ public class LayoutEngine {
private static float getMargin(CSSNode node, PositionIndex position) {
switch (position) {
case TOP:
return node.style.margin[Spacing.TOP];
return node.style.margin.get(Spacing.TOP);
case BOTTOM:
return node.style.margin[Spacing.BOTTOM];
return node.style.margin.get(Spacing.BOTTOM);
case LEFT:
return node.style.margin[Spacing.LEFT];
return node.style.margin.get(Spacing.LEFT);
case RIGHT:
return node.style.margin[Spacing.RIGHT];
return node.style.margin.get(Spacing.RIGHT);
case START:
return node.style.margin.get(Spacing.START);
case END:
return node.style.margin.get(Spacing.END);
default:
throw new RuntimeException("Someone added a new cardinal direction...");
}
}
private static float getLeadingMargin(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float leadingMargin = getMargin(node, PositionIndex.START);
if (!CSSConstants.isUndefined(leadingMargin)) {
return leadingMargin;
}
}
return getMargin(node, getLeading(axis));
}
private static float getTrailingMargin(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float trailingMargin = getMargin(node, PositionIndex.END);
if (!CSSConstants.isUndefined(trailingMargin)) {
return trailingMargin;
}
}
return getMargin(node, getTrailing(axis));
}
private static float getPadding(CSSNode node, PositionIndex position) {
switch (position) {
case TOP:
return node.style.padding[Spacing.TOP];
return node.style.padding.get(Spacing.TOP);
case BOTTOM:
return node.style.padding[Spacing.BOTTOM];
return node.style.padding.get(Spacing.BOTTOM);
case LEFT:
return node.style.padding[Spacing.LEFT];
return node.style.padding.get(Spacing.LEFT);
case RIGHT:
return node.style.padding[Spacing.RIGHT];
return node.style.padding.get(Spacing.RIGHT);
case START:
return node.style.padding.get(Spacing.START);
case END:
return node.style.padding.get(Spacing.END);
default:
throw new RuntimeException("Someone added a new cardinal direction...");
}
}
private static float getLeadingPadding(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float leadingPadding = getPadding(node, PositionIndex.START);
if (!CSSConstants.isUndefined(leadingPadding)) {
return leadingPadding;
}
}
return getPadding(node, getLeading(axis));
}
private static float getTrailingPadding(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float trailingPadding = getPadding(node, PositionIndex.END);
if (!CSSConstants.isUndefined(trailingPadding)) {
return trailingPadding;
}
}
return getPadding(node, getTrailing(axis));
}
private static float getBorder(CSSNode node, PositionIndex position) {
switch (position) {
case TOP:
return node.style.border[Spacing.TOP];
return node.style.border.get(Spacing.TOP);
case BOTTOM:
return node.style.border[Spacing.BOTTOM];
return node.style.border.get(Spacing.BOTTOM);
case LEFT:
return node.style.border[Spacing.LEFT];
return node.style.border.get(Spacing.LEFT);
case RIGHT:
return node.style.border[Spacing.RIGHT];
return node.style.border.get(Spacing.RIGHT);
case START:
return node.style.border.get(Spacing.START);
case END:
return node.style.border.get(Spacing.END);
default:
throw new RuntimeException("Someone added a new cardinal direction...");
}
}
private static float getPaddingAndBorder(CSSNode node, PositionIndex position) {
return getPadding(node, position) + getBorder(node, position);
private static float getLeadingBorder(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float leadingBorder = getBorder(node, PositionIndex.START);
if (!CSSConstants.isUndefined(leadingBorder)) {
return leadingBorder;
}
}
return getBorder(node, getLeading(axis));
}
private static float getTrailingBorder(CSSNode node, CSSFlexDirection axis) {
if (isRowDirection(axis)) {
float trailingBorder = getBorder(node, PositionIndex.END);
if (!CSSConstants.isUndefined(trailingBorder)) {
return trailingBorder;
}
}
return getBorder(node, getTrailing(axis));
}
private static float getLeadingPaddingAndBorder(CSSNode node, CSSFlexDirection axis) {
return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);
}
private static float getTrailingPaddingAndBorder(CSSNode node, CSSFlexDirection axis) {
return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);
}
private static float getBorderAxis(CSSNode node, CSSFlexDirection axis) {
return getBorder(node, getLeading(axis)) + getBorder(node, getTrailing(axis));
return getLeadingBorder(node, axis) + getTrailingBorder(node, axis);
}
private static float getMarginAxis(CSSNode node, CSSFlexDirection axis) {
return getMargin(node, getLeading(axis)) + getMargin(node, getTrailing(axis));
return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);
}
private static float getPaddingAndBorderAxis(CSSNode node, CSSFlexDirection axis) {
return getPaddingAndBorder(
node,
getLeading(axis)) + getPaddingAndBorder(node, getTrailing(axis));
return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis);
}
private static float boundAxis(CSSNode node, CSSFlexDirection axis, float value) {
float min = CSSConstants.UNDEFINED;
float max = CSSConstants.UNDEFINED;
if (axis == CSSFlexDirection.COLUMN) {
if (isColumnDirection(axis)) {
min = node.style.minHeight;
max = node.style.maxHeight;
} else if (axis == CSSFlexDirection.ROW) {
} else if (isRowDirection(axis)) {
min = node.style.minWidth;
max = node.style.maxWidth;
}
@@ -233,6 +367,18 @@ public class LayoutEngine {
setLayoutDimension(node, getDim(axis), maxLayoutDimension);
}
private static void setTrailingPosition(
CSSNode node,
CSSNode child,
CSSFlexDirection axis) {
setLayoutPosition(
child,
getTrailing(axis),
getLayoutDimension(node, getDim(axis)) -
getLayoutDimension(child, getDim(axis)) -
getLayoutPosition(child, getPos(axis)));
}
private static float getRelativePosition(CSSNode node, CSSFlexDirection axis) {
float lead = getStylePosition(node, getLeading(axis));
if (!CSSConstants.isUndefined(lead)) {
@@ -245,10 +391,53 @@ public class LayoutEngine {
return node.style.flex;
}
private static boolean isRowDirection(CSSFlexDirection flexDirection) {
return flexDirection == CSSFlexDirection.ROW ||
flexDirection == CSSFlexDirection.ROW_REVERSE;
}
private static boolean isColumnDirection(CSSFlexDirection flexDirection) {
return flexDirection == CSSFlexDirection.COLUMN ||
flexDirection == CSSFlexDirection.COLUMN_REVERSE;
}
private static CSSFlexDirection resolveAxis(
CSSFlexDirection axis,
CSSDirection direction) {
if (direction == CSSDirection.RTL) {
if (axis == CSSFlexDirection.ROW) {
return CSSFlexDirection.ROW_REVERSE;
} else if (axis == CSSFlexDirection.ROW_REVERSE) {
return CSSFlexDirection.ROW;
}
}
return axis;
}
private static CSSDirection resolveDirection(CSSNode node, CSSDirection parentDirection) {
CSSDirection direction = node.style.direction;
if (direction == CSSDirection.INHERIT) {
direction = (parentDirection == null ? CSSDirection.LTR : parentDirection);
}
return direction;
}
private static CSSFlexDirection getFlexDirection(CSSNode node) {
return node.style.flexDirection;
}
private static CSSFlexDirection getCrossFlexDirection(
CSSFlexDirection flexDirection,
CSSDirection direction) {
if (isColumnDirection(flexDirection)) {
return resolveAxis(CSSFlexDirection.ROW, direction);
} else {
return CSSFlexDirection.COLUMN;
}
}
private static CSSPositionType getPositionType(CSSNode node) {
return node.style.positionType;
}
@@ -282,8 +471,8 @@ public class LayoutEngine {
private static float getDimWithMargin(CSSNode node, CSSFlexDirection axis) {
return getLayoutDimension(node, getDim(axis)) +
getMargin(node, getLeading(axis)) +
getMargin(node, getTrailing(axis));
getLeadingMargin(node, axis) +
getTrailingMargin(node, axis);
}
private static boolean needsRelayout(CSSNode node, float parentMaxWidth) {
@@ -296,13 +485,14 @@ public class LayoutEngine {
/*package*/ static void layoutNode(
CSSLayoutContext layoutContext,
CSSNode node,
float parentMaxWidth) {
float parentMaxWidth,
CSSDirection parentDirection) {
if (needsRelayout(node, parentMaxWidth)) {
node.lastLayout.requestedWidth = node.layout.width;
node.lastLayout.requestedHeight = node.layout.height;
node.lastLayout.parentMaxWidth = parentMaxWidth;
layoutNodeImpl(layoutContext, node, parentMaxWidth);
layoutNodeImpl(layoutContext, node, parentMaxWidth, parentDirection);
node.lastLayout.copy(node.layout);
} else {
node.layout.copy(node.lastLayout);
@@ -314,18 +504,18 @@ public class LayoutEngine {
private static void layoutNodeImpl(
CSSLayoutContext layoutContext,
CSSNode node,
float parentMaxWidth) {
float parentMaxWidth,
CSSDirection parentDirection) {
for (int i = 0; i < node.getChildCount(); i++) {
node.getChildAt(i).layout.resetResult();
}
/** START_GENERATED **/
CSSFlexDirection mainAxis = getFlexDirection(node);
CSSFlexDirection crossAxis = mainAxis == CSSFlexDirection.ROW ?
CSSFlexDirection.COLUMN :
CSSFlexDirection.ROW;
CSSDirection direction = resolveDirection(node, parentDirection);
CSSFlexDirection mainAxis = resolveAxis(getFlexDirection(node), direction);
CSSFlexDirection crossAxis = getCrossFlexDirection(mainAxis, direction);
CSSFlexDirection resolvedRowAxis = resolveAxis(CSSFlexDirection.ROW, direction);
// Handle width and height style attributes
setDimensionFromStyle(node, mainAxis);
@@ -333,28 +523,32 @@ public class LayoutEngine {
// 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
setLayoutPosition(node, getLeading(mainAxis), getLayoutPosition(node, getLeading(mainAxis)) + getMargin(node, getLeading(mainAxis)) +
setLayoutPosition(node, getLeading(mainAxis), getLayoutPosition(node, getLeading(mainAxis)) + getLeadingMargin(node, mainAxis) +
getRelativePosition(node, mainAxis));
setLayoutPosition(node, getLeading(crossAxis), getLayoutPosition(node, getLeading(crossAxis)) + getMargin(node, getLeading(crossAxis)) +
setLayoutPosition(node, getTrailing(mainAxis), getLayoutPosition(node, getTrailing(mainAxis)) + getTrailingMargin(node, mainAxis) +
getRelativePosition(node, mainAxis));
setLayoutPosition(node, getLeading(crossAxis), getLayoutPosition(node, getLeading(crossAxis)) + getLeadingMargin(node, crossAxis) +
getRelativePosition(node, crossAxis));
setLayoutPosition(node, getTrailing(crossAxis), getLayoutPosition(node, getTrailing(crossAxis)) + getTrailingMargin(node, crossAxis) +
getRelativePosition(node, crossAxis));
if (isMeasureDefined(node)) {
float width = CSSConstants.UNDEFINED;
if (isDimDefined(node, CSSFlexDirection.ROW)) {
if (isDimDefined(node, resolvedRowAxis)) {
width = node.style.width;
} else if (!CSSConstants.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.ROW)))) {
width = getLayoutDimension(node, getDim(CSSFlexDirection.ROW));
} else if (!CSSConstants.isUndefined(getLayoutDimension(node, getDim(resolvedRowAxis)))) {
width = getLayoutDimension(node, getDim(resolvedRowAxis));
} else {
width = parentMaxWidth -
getMarginAxis(node, CSSFlexDirection.ROW);
getMarginAxis(node, resolvedRowAxis);
}
width -= getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
width -= getPaddingAndBorderAxis(node, resolvedRowAxis);
// We only need to give a dimension for the text if we haven't got any
// for it computed yet. It can either be from the style attribute or because
// the element is flexible.
boolean isRowUndefined = !isDimDefined(node, CSSFlexDirection.ROW) &&
CSSConstants.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.ROW)));
boolean isRowUndefined = !isDimDefined(node, resolvedRowAxis) &&
CSSConstants.isUndefined(getLayoutDimension(node, getDim(resolvedRowAxis)));
boolean isColumnUndefined = !isDimDefined(node, CSSFlexDirection.COLUMN) &&
CSSConstants.isUndefined(getLayoutDimension(node, getDim(CSSFlexDirection.COLUMN)));
@@ -366,14 +560,16 @@ public class LayoutEngine {
);
if (isRowUndefined) {
node.layout.width = measureDim.width +
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
getPaddingAndBorderAxis(node, resolvedRowAxis);
}
if (isColumnUndefined) {
node.layout.height = measureDim.height +
getPaddingAndBorderAxis(node, CSSFlexDirection.COLUMN);
}
}
return;
if (node.getChildCount() == 0) {
return;
}
}
int i;
@@ -470,20 +666,20 @@ public class LayoutEngine {
} else {
maxWidth = CSSConstants.UNDEFINED;
if (mainAxis != CSSFlexDirection.ROW) {
if (!isRowDirection(mainAxis)) {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSSFlexDirection.ROW) -
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
getMarginAxis(node, resolvedRowAxis) -
getPaddingAndBorderAxis(node, resolvedRowAxis);
if (isDimDefined(node, CSSFlexDirection.ROW)) {
maxWidth = getLayoutDimension(node, getDim(CSSFlexDirection.ROW)) -
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
if (isDimDefined(node, resolvedRowAxis)) {
maxWidth = getLayoutDimension(node, getDim(resolvedRowAxis)) -
getPaddingAndBorderAxis(node, resolvedRowAxis);
}
}
// This is the main recursive call. We layout non flexible children.
if (alreadyComputedNextLayout == 0) {
layoutNode(layoutContext, child, maxWidth);
layoutNode(layoutContext, child, maxWidth, direction);
}
// Absolute positioned elements do not take part of the layout, so we
@@ -502,6 +698,7 @@ public class LayoutEngine {
// If there's only one element, then it's bigger than the content
// and needs its own line
i != startLine) {
nonFlexibleChildrenCount--;
alreadyComputedNextLayout = 1;
break;
}
@@ -569,17 +766,17 @@ public class LayoutEngine {
));
maxWidth = CSSConstants.UNDEFINED;
if (isDimDefined(node, CSSFlexDirection.ROW)) {
maxWidth = getLayoutDimension(node, getDim(CSSFlexDirection.ROW)) -
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
} else if (mainAxis != CSSFlexDirection.ROW) {
if (isDimDefined(node, resolvedRowAxis)) {
maxWidth = getLayoutDimension(node, getDim(resolvedRowAxis)) -
getPaddingAndBorderAxis(node, resolvedRowAxis);
} else if (!isRowDirection(mainAxis)) {
maxWidth = parentMaxWidth -
getMarginAxis(node, CSSFlexDirection.ROW) -
getPaddingAndBorderAxis(node, CSSFlexDirection.ROW);
getMarginAxis(node, resolvedRowAxis) -
getPaddingAndBorderAxis(node, resolvedRowAxis);
}
// And we recursively call the layout algorithm for this child
layoutNode(layoutContext, child, maxWidth);
layoutNode(layoutContext, child, maxWidth, direction);
}
}
@@ -615,7 +812,7 @@ public class LayoutEngine {
// container!
float crossDim = 0;
float mainDim = leadingMainDim +
getPaddingAndBorder(node, getLeading(mainAxis));
getLeadingPaddingAndBorder(node, mainAxis);
for (i = startLine; i < endLine; ++i) {
child = node.getChildAt(i);
@@ -627,12 +824,17 @@ public class LayoutEngine {
// defined, we override the position to whatever the user said
// (and margin/border).
setLayoutPosition(child, getPos(mainAxis), getPosition(child, getLeading(mainAxis)) +
getBorder(node, getLeading(mainAxis)) +
getMargin(child, getLeading(mainAxis)));
getLeadingBorder(node, mainAxis) +
getLeadingMargin(child, mainAxis));
} else {
// If the child is position absolute (without top/left) or relative,
// we put it at the current accumulated offset.
setLayoutPosition(child, getPos(mainAxis), getLayoutPosition(child, getPos(mainAxis)) + mainDim);
// Define the trailing position accordingly.
if (!CSSConstants.isUndefined(getLayoutDimension(node, getDim(mainAxis)))) {
setTrailingPosition(node, child, mainAxis);
}
}
// Now that we placed the element, we need to update the variables
@@ -660,7 +862,6 @@ public class LayoutEngine {
}
// <Loop D> Position elements in the cross axis
for (i = startLine; i < endLine; ++i) {
child = node.getChildAt(i);
@@ -670,11 +871,11 @@ public class LayoutEngine {
// top/left/bottom/right being set, we override all the previously
// computed positions to set it correctly.
setLayoutPosition(child, getPos(crossAxis), getPosition(child, getLeading(crossAxis)) +
getBorder(node, getLeading(crossAxis)) +
getMargin(child, getLeading(crossAxis)));
getLeadingBorder(node, crossAxis) +
getLeadingMargin(child, crossAxis));
} else {
float leadingCrossDim = getPaddingAndBorder(node, getLeading(crossAxis));
float leadingCrossDim = getLeadingPaddingAndBorder(node, crossAxis);
// For a relative children, we're either using alignItems (parent) or
// alignSelf (child) in order to determine the position in the cross axis
@@ -709,6 +910,11 @@ public class LayoutEngine {
// And we apply the position
setLayoutPosition(child, getPos(crossAxis), getLayoutPosition(child, getPos(crossAxis)) + linesCrossDim + leadingCrossDim);
// Define the trailing position accordingly.
if (!CSSConstants.isUndefined(getLayoutDimension(node, getDim(crossAxis)))) {
setTrailingPosition(node, child, crossAxis);
}
}
}
@@ -735,19 +941,19 @@ public class LayoutEngine {
!CSSConstants.isUndefined(getLayoutDimension(node, getDim(crossAxis)))) {
float nodeCrossAxisInnerSize = getLayoutDimension(node, getDim(crossAxis)) -
getPaddingAndBorderAxis(node, crossAxis);
float remainingCrossDim = nodeCrossAxisInnerSize - linesCrossDim;
float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
float crossDimLead = 0;
float currentLead = getPaddingAndBorder(node, getLeading(crossAxis));
float currentLead = getLeadingPaddingAndBorder(node, crossAxis);
CSSAlign alignContent = getAlignContent(node);
if (alignContent == CSSAlign.FLEX_END) {
currentLead = currentLead + remainingCrossDim;
currentLead = currentLead + remainingAlignContentDim;
} else if (alignContent == CSSAlign.CENTER) {
currentLead = currentLead + remainingCrossDim / 2;
currentLead = currentLead + remainingAlignContentDim / 2;
} else if (alignContent == CSSAlign.STRETCH) {
if (nodeCrossAxisInnerSize > linesCrossDim) {
crossDimLead = (remainingCrossDim / linesCount);
crossDimLead = (remainingAlignContentDim / linesCount);
}
}
@@ -781,16 +987,16 @@ public class LayoutEngine {
continue;
}
CSSAlign alignItem = getAlignItem(node, child);
if (alignItem == CSSAlign.FLEX_START) {
setLayoutPosition(child, getPos(crossAxis), currentLead + getMargin(child, getLeading(crossAxis)));
} else if (alignItem == CSSAlign.FLEX_END) {
setLayoutPosition(child, getPos(crossAxis), currentLead + lineHeight - getMargin(child,getTrailing(crossAxis)) - getLayoutDimension(child, getDim(crossAxis)));
} else if (alignItem == CSSAlign.CENTER) {
CSSAlign alignContentAlignItem = getAlignItem(node, child);
if (alignContentAlignItem == CSSAlign.FLEX_START) {
setLayoutPosition(child, getPos(crossAxis), currentLead + getLeadingMargin(child, crossAxis));
} else if (alignContentAlignItem == CSSAlign.FLEX_END) {
setLayoutPosition(child, getPos(crossAxis), currentLead + lineHeight - getTrailingMargin(child, crossAxis) - getLayoutDimension(child, getDim(crossAxis)));
} else if (alignContentAlignItem == CSSAlign.CENTER) {
float childHeight = getLayoutDimension(child, getDim(crossAxis));
setLayoutPosition(child, getPos(crossAxis), currentLead + (lineHeight - childHeight) / 2);
} else if (alignItem == CSSAlign.STRETCH) {
setLayoutPosition(child, getPos(crossAxis), currentLead + getMargin(child, getLeading(crossAxis)));
} else if (alignContentAlignItem == CSSAlign.STRETCH) {
setLayoutPosition(child, getPos(crossAxis), currentLead + getLeadingMargin(child, crossAxis));
// TODO(prenaux): Correctly set the height of items with undefined
// (auto) crossAxis dimension.
}
@@ -800,16 +1006,21 @@ public class LayoutEngine {
}
}
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 (CSSConstants.isUndefined(getLayoutDimension(node, getDim(mainAxis)))) {
setLayoutDimension(node, getDim(mainAxis), Math.max(
// We're missing the last padding at this point to get the final
// dimension
boundAxis(node, mainAxis, linesMainDim + getPaddingAndBorder(node, getTrailing(mainAxis))),
boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
// We can never assign a width smaller than the padding and borders
getPaddingAndBorderAxis(node, mainAxis)
));
needsMainTrailingPos = true;
}
if (CSSConstants.isUndefined(getLayoutDimension(node, getDim(crossAxis)))) {
@@ -820,9 +1031,26 @@ public class LayoutEngine {
boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
getPaddingAndBorderAxis(node, crossAxis)
));
needsCrossTrailingPos = true;
}
// <Loop F> Calculate dimensions for absolutely positioned elements
// <Loop F> Set trailing position if necessary
if (needsMainTrailingPos || needsCrossTrailingPos) {
for (i = 0; i < node.getChildCount(); ++i) {
child = node.getChildAt(i);
if (needsMainTrailingPos) {
setTrailingPosition(node, child, mainAxis);
}
if (needsCrossTrailingPos) {
setTrailingPosition(node, child, crossAxis);
}
}
}
// <Loop G> Calculate dimensions for absolutely positioned elements
for (i = 0; i < node.getChildCount(); ++i) {
child = node.getChildAt(i);
if (getPositionType(child) == CSSPositionType.ABSOLUTE) {

View File

@@ -8,28 +8,124 @@
*/
package com.facebook.csslayout;
import javax.annotation.Nullable;
/**
* Utility class for handling CSS spacing (padding, margin, and borders). This is mostly necessary
* to properly implement interactions and updates for properties like margin, marginLeft, and
* marginHorizontal. This is not a great API and should probably be updated to use actual objects
* for type safety, defaults safety, and simplicity.
* Class representing CSS spacing (padding, margin, and borders). This is mostly necessary to
* properly implement interactions and updates for properties like margin, marginLeft, and
* marginHorizontal.
*/
public class Spacing {
// Indices into FullSpacingArray and SpacingResultArray
/**
* Spacing type that represents the left direction. E.g. {@code marginLeft}.
*/
public static final int LEFT = 0;
/**
* Spacing type that represents the top direction. E.g. {@code marginTop}.
*/
public static final int TOP = 1;
/**
* Spacing type that represents the right direction. E.g. {@code marginRight}.
*/
public static final int RIGHT = 2;
/**
* Spacing type that represents the bottom direction. E.g. {@code marginBottom}.
*/
public static final int BOTTOM = 3;
/**
* Spacing type that represents vertical direction (top and bottom). E.g. {@code marginVertical}.
*/
public static final int VERTICAL = 4;
/**
* Spacing type that represents horizontal direction (left and right). E.g.
* {@code marginHorizontal}.
*/
public static final int HORIZONTAL = 5;
public static final int ALL = 6;
/**
* Spacing type that represents start direction e.g. left in left-to-right, right in right-to-left.
*/
public static final int START = 6;
/**
* Spacing type that represents end direction e.g. right in left-to-right, left in right-to-left.
*/
public static final int END = 7;
/**
* Spacing type that represents all directions (left, top, right, bottom). E.g. {@code margin}.
*/
public static final int ALL = 8;
private final float[] mSpacing = newFullSpacingArray();
@Nullable private float[] mDefaultSpacing = null;
/**
* @return an instance of an array that can be used with {@link #updateSpacing}. Stores
* the value for each spacing type or NaN if it hasn't been explicitly set.
* Set a spacing value.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
* @param value the value for this direction
* @return {@code true} if the spacing has changed, or {@code false} if the same value was already
* set
*/
public static float[] newFullSpacingArray() {
public boolean set(int spacingType, float value) {
if (!FloatUtil.floatsEqual(mSpacing[spacingType], value)) {
mSpacing[spacingType] = value;
return true;
}
return false;
}
/**
* Set a default spacing value. This is used as a fallback when no spacing has been set for a
* particular direction.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
* @param value the default value for this direction
* @return
*/
public boolean setDefault(int spacingType, float value) {
if (mDefaultSpacing == null) {
mDefaultSpacing = newSpacingResultArray();
}
if (!FloatUtil.floatsEqual(mDefaultSpacing[spacingType], value)) {
mDefaultSpacing[spacingType] = value;
return true;
}
return false;
}
/**
* Get the spacing for a direction. This takes into account any default values that have been set.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM}
*/
public float get(int spacingType) {
int secondType = spacingType == TOP || spacingType == BOTTOM ? VERTICAL : HORIZONTAL;
float defaultValue = spacingType == START || spacingType == END ? CSSConstants.UNDEFINED : 0;
return
!CSSConstants.isUndefined(mSpacing[spacingType])
? mSpacing[spacingType]
: !CSSConstants.isUndefined(mSpacing[secondType])
? mSpacing[secondType]
: !CSSConstants.isUndefined(mSpacing[ALL])
? mSpacing[ALL]
: mDefaultSpacing != null
? mDefaultSpacing[spacingType]
: defaultValue;
}
/**
* Get the raw value (that was set using {@link #set(int, float)}), without taking into account
* any default values.
*
* @param spacingType one of {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, {@link #BOTTOM},
* {@link #VERTICAL}, {@link #HORIZONTAL}, {@link #ALL}
*/
public float getRaw(int spacingType) {
return mSpacing[spacingType];
}
private static float[] newFullSpacingArray() {
return new float[] {
CSSConstants.UNDEFINED,
CSSConstants.UNDEFINED,
@@ -39,65 +135,25 @@ public class Spacing {
CSSConstants.UNDEFINED,
CSSConstants.UNDEFINED,
CSSConstants.UNDEFINED,
CSSConstants.UNDEFINED,
};
}
/**
* @return {@link #newSpacingResultArray} filled with zero.
*/
public static float[] newSpacingResultArray() {
private static float[] newSpacingResultArray() {
return newSpacingResultArray(0);
}
/**
* @return an instance of an array used to store the end result of the interactions between the
* values in a full spacing array. Use {@link #TOP}, etc to access result values.
*/
public static float[] newSpacingResultArray(float defaultValue) {
private static float[] newSpacingResultArray(float defaultValue) {
return new float[] {
defaultValue,
defaultValue,
defaultValue,
defaultValue,
defaultValue,
defaultValue,
CSSConstants.UNDEFINED,
CSSConstants.UNDEFINED,
defaultValue,
};
}
/**
* Given the fullSpacing from {@link #newFullSpacingArray()} and the spacingResult from
* {@link #newSpacingResultArray()} from a View, update them both to reflect a new value for the
* given spacingType (e.g. {@link #TOP}). defaultValue specifies the result value that should be
* used whenever a spacing property hasn't been set.
*/
public static void updateSpacing(
float[] fullSpacing,
float[] spacingResult,
int spacingType,
float value,
float defaultValue) {
fullSpacing[spacingType] = value;
spacingResult[Spacing.TOP] =
!CSSConstants.isUndefined(fullSpacing[Spacing.TOP]) ? fullSpacing[Spacing.TOP]
: !CSSConstants.isUndefined(fullSpacing[Spacing.VERTICAL]) ?
fullSpacing[Spacing.VERTICAL]
: !CSSConstants.isUndefined(fullSpacing[Spacing.ALL]) ? fullSpacing[Spacing.ALL]
: defaultValue;
spacingResult[Spacing.BOTTOM] =
!CSSConstants.isUndefined(fullSpacing[Spacing.BOTTOM]) ? fullSpacing[Spacing.BOTTOM]
: !CSSConstants.isUndefined(fullSpacing[Spacing.VERTICAL]) ?
fullSpacing[Spacing.VERTICAL]
: !CSSConstants.isUndefined(fullSpacing[Spacing.ALL]) ? fullSpacing[Spacing.ALL]
: defaultValue;
spacingResult[Spacing.LEFT] =
!CSSConstants.isUndefined(fullSpacing[Spacing.LEFT]) ? fullSpacing[Spacing.LEFT]
: !CSSConstants.isUndefined(fullSpacing[Spacing.HORIZONTAL]) ?
fullSpacing[Spacing.HORIZONTAL]
: !CSSConstants.isUndefined(fullSpacing[Spacing.ALL]) ? fullSpacing[Spacing.ALL]
: defaultValue;
spacingResult[Spacing.RIGHT] =
!CSSConstants.isUndefined(fullSpacing[Spacing.RIGHT]) ? fullSpacing[Spacing.RIGHT]
: !CSSConstants.isUndefined(fullSpacing[Spacing.HORIZONTAL]) ?
fullSpacing[Spacing.HORIZONTAL]
: !CSSConstants.isUndefined(fullSpacing[Spacing.ALL]) ? fullSpacing[Spacing.ALL]
: defaultValue;
}
}

File diff suppressed because it is too large Load Diff