mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-08 09:12:05 +08:00
This is an early release and there are several things that are known not to work if you're porting your iOS app to Android. See the Known Issues guide on the website. We will work with the community to reach platform parity with iOS.
397 lines
10 KiB
Java
397 lines
10 KiB
Java
/**
|
|
* 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.
|
|
*/
|
|
|
|
// NOTE: this file is auto-copied from https://github.com/facebook/css-layout
|
|
// @generated SignedSource<<c3a1298e36789dcda4cc2776d48646a7>>
|
|
|
|
package com.facebook.csslayout;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import com.facebook.infer.annotation.Assertions;
|
|
|
|
/**
|
|
* 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 CSSNode {
|
|
|
|
private static enum LayoutState {
|
|
/**
|
|
* 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.
|
|
*/
|
|
HAS_NEW_LAYOUT,
|
|
|
|
/**
|
|
* {@link #layout} is valid for the node's properties and this layout has been marked as
|
|
* having been seen.
|
|
*/
|
|
UP_TO_DATE,
|
|
}
|
|
|
|
public static interface MeasureFunction {
|
|
|
|
/**
|
|
* Should measure the given node and put the result in the given MeasureOutput.
|
|
*
|
|
* NB: measure is NOT guaranteed to be threadsafe/re-entrant safe!
|
|
*/
|
|
public void measure(CSSNode node, float width, MeasureOutput measureOutput);
|
|
}
|
|
|
|
// VisibleForTesting
|
|
/*package*/ final CSSStyle style = new CSSStyle();
|
|
/*package*/ final CSSLayout layout = new CSSLayout();
|
|
/*package*/ final CachedCSSLayout lastLayout = new CachedCSSLayout();
|
|
|
|
public int lineIndex = 0;
|
|
|
|
private @Nullable ArrayList<CSSNode> mChildren;
|
|
private @Nullable CSSNode mParent;
|
|
private @Nullable MeasureFunction mMeasureFunction = null;
|
|
private LayoutState mLayoutState = LayoutState.DIRTY;
|
|
|
|
public int getChildCount() {
|
|
return mChildren == null ? 0 : mChildren.size();
|
|
}
|
|
|
|
public CSSNode getChildAt(int i) {
|
|
Assertions.assertNotNull(mChildren);
|
|
return mChildren.get(i);
|
|
}
|
|
|
|
public void addChildAt(CSSNode child, int i) {
|
|
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);
|
|
}
|
|
|
|
mChildren.add(i, child);
|
|
child.mParent = this;
|
|
dirty();
|
|
}
|
|
|
|
public CSSNode removeChildAt(int i) {
|
|
Assertions.assertNotNull(mChildren);
|
|
CSSNode removed = mChildren.remove(i);
|
|
removed.mParent = null;
|
|
dirty();
|
|
return removed;
|
|
}
|
|
|
|
public @Nullable CSSNode getParent() {
|
|
return mParent;
|
|
}
|
|
|
|
/**
|
|
* @return the index of the given child, or -1 if the child doesn't exist in this node.
|
|
*/
|
|
public int indexOf(CSSNode child) {
|
|
Assertions.assertNotNull(mChildren);
|
|
return mChildren.indexOf(child);
|
|
}
|
|
|
|
public void setMeasureFunction(MeasureFunction measureFunction) {
|
|
if (!valuesEqual(mMeasureFunction, measureFunction)) {
|
|
mMeasureFunction = measureFunction;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public boolean isMeasureDefined() {
|
|
return mMeasureFunction != null;
|
|
}
|
|
|
|
/*package*/ MeasureOutput measure(MeasureOutput measureOutput, float width) {
|
|
if (!isMeasureDefined()) {
|
|
throw new RuntimeException("Measure function isn't defined!");
|
|
}
|
|
measureOutput.height = CSSConstants.UNDEFINED;
|
|
measureOutput.width = CSSConstants.UNDEFINED;
|
|
Assertions.assertNotNull(mMeasureFunction).measure(this, width, measureOutput);
|
|
return measureOutput;
|
|
}
|
|
|
|
/**
|
|
* Performs the actual layout and saves the results in {@link #layout}
|
|
*/
|
|
public void calculateLayout(CSSLayoutContext layoutContext) {
|
|
layout.resetResult();
|
|
LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, null);
|
|
}
|
|
|
|
/**
|
|
* See {@link LayoutState#DIRTY}.
|
|
*/
|
|
protected boolean isDirty() {
|
|
return mLayoutState == LayoutState.DIRTY;
|
|
}
|
|
|
|
/**
|
|
* See {@link LayoutState#HAS_NEW_LAYOUT}.
|
|
*/
|
|
public boolean hasNewLayout() {
|
|
return mLayoutState == LayoutState.HAS_NEW_LAYOUT;
|
|
}
|
|
|
|
protected void dirty() {
|
|
if (mLayoutState == LayoutState.DIRTY) {
|
|
return;
|
|
} else if (mLayoutState == LayoutState.HAS_NEW_LAYOUT) {
|
|
throw new IllegalStateException("Previous layout was ignored! markLayoutSeen() never called");
|
|
}
|
|
|
|
mLayoutState = LayoutState.DIRTY;
|
|
|
|
if (mParent != null) {
|
|
mParent.dirty();
|
|
}
|
|
}
|
|
|
|
/*package*/ void markHasNewLayout() {
|
|
mLayoutState = LayoutState.HAS_NEW_LAYOUT;
|
|
}
|
|
|
|
/**
|
|
* Tells the node that the current values in {@link #layout} have been seen. Subsequent calls
|
|
* 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.
|
|
*/
|
|
public void markLayoutSeen() {
|
|
if (!hasNewLayout()) {
|
|
throw new IllegalStateException("Expected node to have a new layout to be seen!");
|
|
}
|
|
|
|
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());
|
|
|
|
if (getChildCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
result.append(", children: [\n");
|
|
for (int i = 0; i < getChildCount(); i++) {
|
|
getChildAt(i).toStringWithIndentation(result, level + 1);
|
|
result.append("\n");
|
|
}
|
|
result.append(indentation + "]");
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
this.toStringWithIndentation(sb, 0);
|
|
return sb.toString();
|
|
}
|
|
|
|
protected boolean valuesEqual(float f1, float f2) {
|
|
return FloatUtil.floatsEqual(f1, f2);
|
|
}
|
|
|
|
protected <T> boolean valuesEqual(@Nullable T o1, @Nullable T o2) {
|
|
if (o1 == null) {
|
|
return o2 == null;
|
|
}
|
|
return o1.equals(o2);
|
|
}
|
|
|
|
public void setDirection(CSSDirection direction) {
|
|
if (!valuesEqual(style.direction, direction)) {
|
|
style.direction = direction;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setFlexDirection(CSSFlexDirection flexDirection) {
|
|
if (!valuesEqual(style.flexDirection, flexDirection)) {
|
|
style.flexDirection = flexDirection;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setJustifyContent(CSSJustify justifyContent) {
|
|
if (!valuesEqual(style.justifyContent, justifyContent)) {
|
|
style.justifyContent = justifyContent;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setAlignItems(CSSAlign alignItems) {
|
|
if (!valuesEqual(style.alignItems, alignItems)) {
|
|
style.alignItems = alignItems;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setAlignSelf(CSSAlign alignSelf) {
|
|
if (!valuesEqual(style.alignSelf, alignSelf)) {
|
|
style.alignSelf = alignSelf;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPositionType(CSSPositionType positionType) {
|
|
if (!valuesEqual(style.positionType, positionType)) {
|
|
style.positionType = positionType;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setWrap(CSSWrap flexWrap) {
|
|
if (!valuesEqual(style.flexWrap, flexWrap)) {
|
|
style.flexWrap = flexWrap;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setFlex(float flex) {
|
|
if (!valuesEqual(style.flex, flex)) {
|
|
style.flex = flex;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setMargin(int spacingType, float margin) {
|
|
if (style.margin.set(spacingType, margin)) {
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPadding(int spacingType, float padding) {
|
|
if (style.padding.set(spacingType, padding)) {
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setBorder(int spacingType, float border) {
|
|
if (style.border.set(spacingType, border)) {
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPositionTop(float positionTop) {
|
|
if (!valuesEqual(style.positionTop, positionTop)) {
|
|
style.positionTop = positionTop;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPositionBottom(float positionBottom) {
|
|
if (!valuesEqual(style.positionBottom, positionBottom)) {
|
|
style.positionBottom = positionBottom;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPositionLeft(float positionLeft) {
|
|
if (!valuesEqual(style.positionLeft, positionLeft)) {
|
|
style.positionLeft = positionLeft;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setPositionRight(float positionRight) {
|
|
if (!valuesEqual(style.positionRight, positionRight)) {
|
|
style.positionRight = positionRight;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setStyleWidth(float width) {
|
|
if (!valuesEqual(style.width, width)) {
|
|
style.width = width;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public void setStyleHeight(float height) {
|
|
if (!valuesEqual(style.height, height)) {
|
|
style.height = height;
|
|
dirty();
|
|
}
|
|
}
|
|
|
|
public float getLayoutX() {
|
|
return layout.left;
|
|
}
|
|
|
|
public float getLayoutY() {
|
|
return layout.top;
|
|
}
|
|
|
|
public float getLayoutWidth() {
|
|
return layout.width;
|
|
}
|
|
|
|
public float getLayoutHeight() {
|
|
return layout.height;
|
|
}
|
|
|
|
public CSSDirection getLayoutDirection() {
|
|
return layout.direction;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Get this node's direction, as defined in the style.
|
|
*/
|
|
public CSSDirection getStyleDirection() {
|
|
return style.direction;
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
}
|
|
}
|