Files
yoga/java/com/facebook/csslayout/CSSNodeDEPRECATED.java

655 lines
15 KiB
Java
Raw Normal View History

/**
* Copyright (c) 2014-present, 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.
*/
2014-09-18 15:15:21 -07:00
package com.facebook.csslayout;
import javax.annotation.Nullable;
2014-09-18 15:15:21 -07:00
import java.util.ArrayList;
import com.facebook.infer.annotation.Assertions;
import static com.facebook.csslayout.CSSLayout.DIMENSION_HEIGHT;
import static com.facebook.csslayout.CSSLayout.DIMENSION_WIDTH;
import static com.facebook.csslayout.CSSLayout.POSITION_LEFT;
import static com.facebook.csslayout.CSSLayout.POSITION_TOP;
2014-09-18 15:15:21 -07:00
/**
* A CSS Node. It has a style object you can manipulate at {@link #style}. After calling
* {@link #calculateLayout()}, {@link #layout} will be filled with the results of the layout.
*/
public class CSSNodeDEPRECATED implements CSSNodeAPI<CSSNodeDEPRECATED> {
2014-09-18 15:15:21 -07:00
private enum LayoutState {
2014-09-18 15:15:21 -07:00
/**
* Some property of this node or its children has changes and the current values in
* {@link #layout} are not valid.
*/
DIRTY,
/**
* This node has a new layout relative to the last time {@link #markLayoutSeen()} was called.
2014-09-18 15:15:21 -07:00
*/
HAS_NEW_LAYOUT,
/**
* {@link #layout} is valid for the node's properties and this layout has been marked as
* having been seen.
2014-09-18 15:15:21 -07:00
*/
UP_TO_DATE,
}
// VisibleForTesting
final CSSStyle style = new CSSStyle();
final CSSLayout layout = new CSSLayout();
final CachedCSSLayout lastLayout = new CachedCSSLayout();
2014-09-18 15:15:21 -07:00
public int lineIndex = 0;
CSSNodeDEPRECATED nextChild;
2016-06-03 22:19:03 +01:00
private @Nullable ArrayList<CSSNodeDEPRECATED> mChildren;
private @Nullable CSSNodeDEPRECATED mParent;
private @Nullable MeasureFunction mMeasureFunction = null;
2014-09-18 15:15:21 -07:00
private LayoutState mLayoutState = LayoutState.DIRTY;
2016-06-03 15:38:08 +01:00
private boolean mIsTextNode = false;
private Object mData;
2014-09-18 15:15:21 -07:00
@Override
public void init() {
free();
}
@Override
2014-09-18 15:15:21 -07:00
public int getChildCount() {
return mChildren == null ? 0 : mChildren.size();
2014-09-18 15:15:21 -07:00
}
@Override
public CSSNodeDEPRECATED getChildAt(int i) {
Assertions.assertNotNull(mChildren);
2014-09-18 15:15:21 -07:00
return mChildren.get(i);
}
@Override
public void addChildAt(CSSNodeDEPRECATED child, int i) {
2014-09-18 15:15:21 -07:00
if (child.mParent != null) {
throw new IllegalStateException("Child already has a parent, it must be removed first.");
}
if (mChildren == null) {
// 4 is kinda arbitrary, but the default of 10 seems really high for an average View.
mChildren = new ArrayList<>(4);
}
2014-09-18 15:15:21 -07:00
mChildren.add(i, child);
child.mParent = this;
dirty();
}
@Override
public CSSNodeDEPRECATED removeChildAt(int i) {
Assertions.assertNotNull(mChildren);
CSSNodeDEPRECATED removed = mChildren.remove(i);
removed.mParent = null;
2014-09-18 15:15:21 -07:00
dirty();
return removed;
2014-09-18 15:15:21 -07:00
}
@Override
public @Nullable
CSSNodeDEPRECATED getParent() {
2014-09-18 15:15:21 -07:00
return mParent;
}
/**
* @return the index of the given child, or -1 if the child doesn't exist in this node.
*/
@Override
public int indexOf(CSSNodeDEPRECATED child) {
Assertions.assertNotNull(mChildren);
return mChildren.indexOf(child);
}
@Override
2014-09-18 15:15:21 -07:00
public void setMeasureFunction(MeasureFunction measureFunction) {
if (mMeasureFunction != measureFunction) {
mMeasureFunction = measureFunction;
dirty();
}
2014-09-18 15:15:21 -07:00
}
@Override
2014-09-18 15:15:21 -07:00
public boolean isMeasureDefined() {
return mMeasureFunction != null;
}
@Override
2016-06-03 15:38:08 +01:00
public void setIsTextNode(boolean isTextNode) {
mIsTextNode = isTextNode;
}
@Override
2016-06-03 15:38:08 +01:00
public boolean isTextNode() {
return mIsTextNode;
}
MeasureOutput measure(MeasureOutput measureOutput, float width, CSSMeasureMode widthMode, float height, CSSMeasureMode heightMode) {
2014-09-18 15:15:21 -07:00
if (!isMeasureDefined()) {
throw new RuntimeException("Measure function isn't defined!");
}
2015-03-23 17:49:47 +00:00
measureOutput.height = CSSConstants.UNDEFINED;
measureOutput.width = CSSConstants.UNDEFINED;
2016-01-06 16:56:56 +00:00
Assertions.assertNotNull(mMeasureFunction).measure(this, width, widthMode, height, heightMode, measureOutput);
2015-03-23 17:49:47 +00:00
return measureOutput;
2014-09-18 15:15:21 -07:00
}
/**
* Performs the actual layout and saves the results in {@link #layout}
*/
@Override
2015-03-23 17:49:47 +00:00
public void calculateLayout(CSSLayoutContext layoutContext) {
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, CSSConstants.UNDEFINED, null);
2014-09-18 15:15:21 -07:00
}
/**
* See {@link LayoutState#DIRTY}.
*/
@Override
public boolean isDirty() {
2014-09-18 15:15:21 -07:00
return mLayoutState == LayoutState.DIRTY;
}
/**
* See {@link LayoutState#HAS_NEW_LAYOUT}.
*/
@Override
2014-09-18 15:15:21 -07:00
public boolean hasNewLayout() {
return mLayoutState == LayoutState.HAS_NEW_LAYOUT;
}
@Override
public void dirty() {
2014-09-18 15:15:21 -07:00
if (mLayoutState == LayoutState.DIRTY) {
return;
} else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT) {
throw new IllegalStateException("Previous layout was ignored! markLayoutSeen() never called");
2014-09-18 15:15:21 -07:00
}
mLayoutState = LayoutState.DIRTY;
layout.computedFlexBasis = CSSConstants.UNDEFINED;
2014-09-18 15:15:21 -07:00
if (mParent != null) {
mParent.dirty();
}
}
void markHasNewLayout() {
2014-09-18 15:15:21 -07:00
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
}
/**
* Tells the node that the current values in {@link #layout} have been seen. Subsequent calls
2014-09-18 15:15:21 -07:00
* to {@link #hasNewLayout()} will return false until this node is laid out with new parameters.
* You must call this each time the layout is generated if the node has a new layout.
*/
@Override
public void markLayoutSeen() {
2014-09-18 15:15:21 -07:00
if (!hasNewLayout()) {
throw new IllegalStateException("Expected node to have a new layout to be seen!");
2014-09-18 15:15:21 -07:00
}
mLayoutState = LayoutState.UP_TO_DATE;
}
private void toStringWithIndentation(StringBuilder result, int level) {
// Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
StringBuilder indentation = new StringBuilder();
for (int i = 0; i < level; ++i) {
indentation.append("__");
}
result.append(indentation.toString());
result.append(layout.toString());
2014-09-18 15:15:21 -07:00
if (getChildCount() == 0) {
return;
2014-09-18 15:15:21 -07:00
}
result.append(", children: [\n");
2014-09-18 15:15:21 -07:00
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).toStringWithIndentation(result, level + 1);
result.append("\n");
2014-09-18 15:15:21 -07:00
}
result.append(indentation + "]");
2014-09-18 15:15:21 -07:00
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
this.toStringWithIndentation(sb, 0);
return sb.toString();
2014-09-18 15:15:21 -07:00
}
@Override
public boolean valuesEqual(float f1, float f2) {
return FloatUtil.floatsEqual(f1, f2);
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's direction, as defined in the style.
*/
@Override
public CSSDirection getStyleDirection() {
return style.direction;
}
@Override
public void setDirection(CSSDirection direction) {
if (style.direction != direction) {
style.direction = direction;
dirty();
}
}
/**
* Get this node's flex direction, as defined by style.
*/
@Override
public CSSFlexDirection getFlexDirection() {
return style.flexDirection;
}
@Override
public void setFlexDirection(CSSFlexDirection flexDirection) {
if (style.flexDirection != flexDirection) {
style.flexDirection = flexDirection;
2014-09-18 15:15:21 -07:00
dirty();
}
}
/**
* Get this node's justify content, as defined by style.
*/
@Override
public CSSJustify getJustifyContent() {
return style.justifyContent;
}
@Override
2014-09-18 15:15:21 -07:00
public void setJustifyContent(CSSJustify justifyContent) {
if (style.justifyContent != justifyContent) {
style.justifyContent = justifyContent;
dirty();
}
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's align items, as defined by style.
*/
@Override
public CSSAlign getAlignItems() {
return style.alignItems;
}
@Override
2014-09-18 15:15:21 -07:00
public void setAlignItems(CSSAlign alignItems) {
if (style.alignItems != alignItems) {
style.alignItems = alignItems;
dirty();
}
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's align items, as defined by style.
*/
@Override
public CSSAlign getAlignSelf() {
return style.alignSelf;
}
@Override
2014-09-18 15:15:21 -07:00
public void setAlignSelf(CSSAlign alignSelf) {
if (style.alignSelf != alignSelf) {
style.alignSelf = alignSelf;
dirty();
}
2014-09-18 15:15:21 -07:00
}
@Override
public CSSAlign getAlignContent() {
return style.alignContent;
}
@Override
public void setAlignContent(CSSAlign alignContent) {
if (style.alignContent != alignContent) {
style.alignContent = alignContent;
dirty();
}
}
/**
* Get this node's position type, as defined by style.
*/
@Override
public CSSPositionType getPositionType() {
return style.positionType;
}
@Override
2014-09-18 15:15:21 -07:00
public void setPositionType(CSSPositionType positionType) {
if (style.positionType != positionType) {
style.positionType = positionType;
dirty();
}
2014-09-18 15:15:21 -07:00
}
@Override
2014-12-12 12:03:31 +00:00
public void setWrap(CSSWrap flexWrap) {
if (style.flexWrap != flexWrap) {
2014-12-12 12:03:31 +00:00
style.flexWrap = flexWrap;
dirty();
}
}
/**
* Get this node's flex, as defined by style.
*/
@Override
public float getFlex() {
if (style.flexGrow > 0) {
return style.flexGrow;
} else if (style.flexShrink > 0) {
return -style.flexShrink;
}
return 0;
}
@Override
2014-09-18 15:15:21 -07:00
public void setFlex(float flex) {
if (CSSConstants.isUndefined(flex) || flex == 0) {
setFlexGrow(0);
setFlexShrink(0);
setFlexBasis(CSSConstants.UNDEFINED);
} else if (flex > 0) {
setFlexGrow(flex);
setFlexShrink(0);
setFlexBasis(0);
} else {
setFlexGrow(0);
setFlexShrink(-flex);
setFlexBasis(CSSConstants.UNDEFINED);
}
}
@Override
public float getFlexGrow() {
return style.flexGrow;
}
@Override
public void setFlexGrow(float flexGrow) {
if (!valuesEqual(style.flexGrow, flexGrow)) {
style.flexGrow = flexGrow;
dirty();
}
}
@Override
public float getFlexShrink() {
return style.flexShrink;
}
@Override
public void setFlexShrink(float flexShrink) {
if (!valuesEqual(style.flexShrink, flexShrink)) {
style.flexShrink = flexShrink;
dirty();
}
2014-09-18 15:15:21 -07:00
}
@Override
public float getFlexBasis() {
return style.flexBasis;
}
@Override
public void setFlexBasis(float flexBasis) {
if (!valuesEqual(style.flexBasis, flexBasis)) {
style.flexBasis = flexBasis;
dirty();
}
}
/**
* Get this node's margin, as defined by style + default margin.
*/
@Override
public Spacing getMargin() {
return style.margin;
}
@Override
public void setMargin(int spacingType, float margin) {
if (style.margin.set(spacingType, margin)) {
dirty();
}
}
/**
* Get this node's padding, as defined by style + default padding.
*/
@Override
public Spacing getPadding() {
return style.padding;
}
@Override
public void setPadding(int spacingType, float padding) {
if (style.padding.set(spacingType, padding)) {
dirty();
}
}
/**
* Get this node's border, as defined by style.
*/
@Override
public Spacing getBorder() {
return style.border;
}
@Override
public void setBorder(int spacingType, float border) {
if (style.border.set(spacingType, border)) {
dirty();
}
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's position, as defined by style.
*/
@Override
public Spacing getPosition() {
return style.position;
}
@Override
public void setPosition(int spacingType, float position) {
if (style.position.set(spacingType, position)) {
dirty();
}
}
/**
* Get this node's width, as defined in the style.
*/
@Override
public float getStyleWidth() {
return style.dimensions[DIMENSION_WIDTH];
}
@Override
2014-09-18 15:15:21 -07:00
public void setStyleWidth(float width) {
if (!valuesEqual(style.dimensions[DIMENSION_WIDTH], width)) {
style.dimensions[DIMENSION_WIDTH] = width;
dirty();
}
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's height, as defined in the style.
*/
@Override
public float getStyleHeight() {
return style.dimensions[DIMENSION_HEIGHT];
}
@Override
2014-09-18 15:15:21 -07:00
public void setStyleHeight(float height) {
if (!valuesEqual(style.dimensions[DIMENSION_HEIGHT], height)) {
style.dimensions[DIMENSION_HEIGHT] = height;
dirty();
}
2014-09-18 15:15:21 -07:00
}
/**
* Get this node's max width, as defined in the style
*/
@Override
public float getStyleMaxWidth() {
return style.maxWidth;
}
@Override
public void setStyleMaxWidth(float maxWidth) {
if (!valuesEqual(style.maxWidth, maxWidth)) {
style.maxWidth = maxWidth;
dirty();
}
}
/**
* Get this node's min width, as defined in the style
*/
@Override
public float getStyleMinWidth() {
return style.minWidth;
}
@Override
public void setStyleMinWidth(float minWidth) {
if (!valuesEqual(style.minWidth, minWidth)) {
style.minWidth = minWidth;
dirty();
}
}
/**
* Get this node's max height, as defined in the style
*/
@Override
public float getStyleMaxHeight() {
return style.maxHeight;
}
@Override
public void setStyleMaxHeight(float maxHeight) {
if (!valuesEqual(style.maxHeight, maxHeight)) {
style.maxHeight = maxHeight;
dirty();
}
}
/**
* Get this node's min height, as defined in the style
*/
@Override
public float getStyleMinHeight() {
return style.minHeight;
}
@Override
public void setStyleMinHeight(float minHeight) {
if (!valuesEqual(style.minHeight, minHeight)) {
style.minHeight = minHeight;
dirty();
}
}
@Override
2014-09-18 15:15:21 -07:00
public float getLayoutX() {
return layout.position[POSITION_LEFT];
2014-09-18 15:15:21 -07:00
}
@Override
2014-09-18 15:15:21 -07:00
public float getLayoutY() {
return layout.position[POSITION_TOP];
2014-09-18 15:15:21 -07:00
}
@Override
2014-09-18 15:15:21 -07:00
public float getLayoutWidth() {
return layout.dimensions[DIMENSION_WIDTH];
2014-09-18 15:15:21 -07:00
}
@Override
2014-09-18 15:15:21 -07:00
public float getLayoutHeight() {
return layout.dimensions[DIMENSION_HEIGHT];
2014-09-18 15:15:21 -07:00
}
@Override
public CSSDirection getLayoutDirection() {
return layout.direction;
}
/**
* Get this node's overflow property, as defined in the style
*/
@Override
public CSSOverflow getOverflow() {
return style.overflow;
}
@Override
public void setOverflow(CSSOverflow overflow) {
if (style.overflow != overflow) {
style.overflow = overflow;
dirty();
}
}
@Override
public void setData(Object data) {
mData = data;
}
@Override
public Object getData() {
return mData;
}
/**
* Resets this instance to its default state. This method is meant to be used when
* recycling {@link CSSNodeDEPRECATED} instances.
*/
@Override
public void free() {
if (mParent != null || (mChildren != null && mChildren.size() > 0)) {
throw new IllegalStateException("You should not free an attached CSSNodeDEPRECATED");
}
style.reset();
layout.resetResult();
lineIndex = 0;
mLayoutState = LayoutState.DIRTY;
mMeasureFunction = null;
}
2014-09-18 15:15:21 -07:00
}