+ * may or may not be embedded in a parent text. There are better solutions that should probably be
* explored, namely using the VirtualText class in JS and setting the correct set of validAttributes
*/
-public class LayoutShadowNode extends ReactShadowNode {
+public class LayoutShadowNode extends ReactShadowNodeImpl {
/**
* A Mutable version of com.facebook.yoga.YogaValue
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java
index 9a33f8a4c..8c6b18be9 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java
@@ -9,476 +9,180 @@
package com.facebook.react.uimanager;
-import javax.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.ArrayList;
-
+import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
import com.facebook.yoga.YogaAlign;
-import com.facebook.yoga.YogaConfig;
-import com.facebook.yoga.YogaDisplay;
-import com.facebook.yoga.YogaEdge;
-import com.facebook.yoga.YogaConstants;
+import com.facebook.yoga.YogaBaselineFunction;
import com.facebook.yoga.YogaDirection;
+import com.facebook.yoga.YogaDisplay;
import com.facebook.yoga.YogaFlexDirection;
import com.facebook.yoga.YogaJustify;
-import com.facebook.yoga.YogaBaselineFunction;
import com.facebook.yoga.YogaMeasureFunction;
import com.facebook.yoga.YogaNode;
import com.facebook.yoga.YogaOverflow;
import com.facebook.yoga.YogaPositionType;
import com.facebook.yoga.YogaValue;
import com.facebook.yoga.YogaWrap;
-import com.facebook.infer.annotation.Assertions;
-import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
+import javax.annotation.Nullable;
/**
- * Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily
- * for layouting therefore it extends {@link YogaNode} to allow that. They also help with handling
+ * Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily for
+ * layouting therefore it extends {@link YogaNode} to allow that. They also help with handling
* Common base subclass of {@link YogaNode} for all layout nodes for react-based view. It extends
* {@link YogaNode} by adding additional capabilities.
*
- * Instances of this class receive property updates from JS via @{link UIManagerModule}. Subclasses
- * may use {@link #updateShadowNode} to persist some of the updated fields in the node instance that
- * corresponds to a particular view type.
+ * Instances of this class receive property updates from JS via @{link UIManagerModule}.
+ * Subclasses may use {@link #updateShadowNode} to persist some of the updated fields in the node
+ * instance that corresponds to a particular view type.
*
- * Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
+ *
Subclasses of {@link ReactShadowNode} should be created only from {@link ViewManager} that
* corresponds to a certain type of native view. They will be updated and accessed only from JS
* thread. Subclasses of {@link ViewManager} may choose to use base class {@link ReactShadowNode} or
* custom subclass of it if necessary.
*
- * The primary use-case for {@link ReactShadowNode} nodes is to calculate layouting. Although this
- * might be extended. For some examples please refer to ARTGroupYogaNode or ReactTextYogaNode.
+ *
The primary use-case for {@link ReactShadowNode} nodes is to calculate layouting. Although
+ * this might be extended. For some examples please refer to ARTGroupYogaNode or ReactTextYogaNode.
*
- * This class allows for the native view hierarchy to not be an exact copy of the hierarchy received
- * from JS by keeping track of both JS children (e.g. {@link #getChildCount()} and separately native
- * children (e.g. {@link #getNativeChildCount()}). See {@link NativeViewHierarchyOptimizer} for more
- * information.
+ *
This class allows for the native view hierarchy to not be an exact copy of the hierarchy
+ * received from JS by keeping track of both JS children (e.g. {@link #getChildCount()} and
+ * separately native children (e.g. {@link #getNativeChildCount()}). See {@link
+ * NativeViewHierarchyOptimizer} for more information.
*/
@ReactPropertyHolder
-public class ReactShadowNode {
-
- private int mReactTag;
- private @Nullable String mViewClassName;
- private @Nullable ReactShadowNode mRootNode;
- private @Nullable ThemedReactContext mThemedContext;
- private boolean mShouldNotifyOnLayout;
- private boolean mNodeUpdated = true;
- private @Nullable ArrayList mChildren;
- private @Nullable ReactShadowNode mParent;
-
- // layout-only nodes
- private boolean mIsLayoutOnly;
- private int mTotalNativeChildren = 0;
- private @Nullable ReactShadowNode mNativeParent;
- private @Nullable ArrayList mNativeChildren;
- private int mScreenX;
- private int mScreenY;
- private int mScreenWidth;
- private int mScreenHeight;
- private final Spacing mDefaultPadding = new Spacing(0);
- private final float[] mPadding = new float[Spacing.ALL + 1];
- private final boolean[] mPaddingIsPercent = new boolean[Spacing.ALL + 1];
- private final YogaNode mYogaNode;
- private static YogaConfig sYogaConfig;
-
- public ReactShadowNode() {
- if (!isVirtual()) {
- YogaNode node = YogaNodePool.get().acquire();
- if (sYogaConfig == null) {
- sYogaConfig = new YogaConfig();
- sYogaConfig.setPointScaleFactor(0f);
- sYogaConfig.setUseLegacyStretchBehaviour(true);
- }
- if (node == null) {
- node = new YogaNode(sYogaConfig);
- }
- mYogaNode = node;
- Arrays.fill(mPadding, YogaConstants.UNDEFINED);
- } else {
- mYogaNode = null;
- }
- }
+public interface ReactShadowNode {
/**
* Nodes that return {@code true} will be treated as "virtual" nodes. That is, nodes that are not
* mapped into native views (e.g. nested text node). By default this method returns {@code false}.
*/
- public boolean isVirtual() {
- return false;
- }
+ boolean isVirtual();
/**
* Nodes that return {@code true} will be treated as a root view for the virtual nodes tree. It
* means that {@link NativeViewHierarchyManager} will not try to perform {@code manageChildren}
- * operation on such views. Good example is {@code InputText} view that may have children
- * {@code Text} nodes but this whole hierarchy will be mapped to a single android {@link EditText}
- * view.
+ * operation on such views. Good example is {@code InputText} view that may have children {@code
+ * Text} nodes but this whole hierarchy will be mapped to a single android {@link EditText} view.
*/
- public boolean isVirtualAnchor() {
- return false;
- }
+ boolean isVirtualAnchor();
/**
- * Nodes that return {@code true} will not manage (and and remove) child Yoga nodes.
- * For example {@link ReactTextInputShadowNode} or {@link ReactTextShadowNode} have child nodes,
- * which do not want Yoga to lay out, so in the eyes of Yoga it is a leaf node.
- * Override this method in subclass to enforce this requirement.
+ * Nodes that return {@code true} will not manage (and and remove) child Yoga nodes. For example
+ * {@link ReactTextInputShadowNode} or {@link ReactTextShadowNode} have child nodes, which do not
+ * want Yoga to lay out, so in the eyes of Yoga it is a leaf node. Override this method in
+ * subclass to enforce this requirement.
*/
- public boolean isYogaLeafNode() {
- return isMeasureDefined();
- }
+ boolean isYogaLeafNode();
- public final String getViewClass() {
- return Assertions.assertNotNull(mViewClassName);
- }
+ String getViewClass();
- public final boolean hasUpdates() {
- return mNodeUpdated || hasNewLayout() || isDirty();
- }
+ boolean hasUpdates();
- public final void markUpdateSeen() {
- mNodeUpdated = false;
- if (hasNewLayout()) {
- markLayoutSeen();
- }
- }
+ void markUpdateSeen();
- public void markUpdated() {
- if (mNodeUpdated) {
- return;
- }
- mNodeUpdated = true;
- ReactShadowNode parent = getParent();
- if (parent != null) {
- parent.markUpdated();
- }
- }
+ void markUpdated();
- public final boolean hasUnseenUpdates() {
- return mNodeUpdated;
- }
+ boolean hasUnseenUpdates();
- public void dirty() {
- if (!isVirtual()) {
- mYogaNode.dirty();
- }
- }
+ void dirty();
- public final boolean isDirty() {
- return mYogaNode != null && mYogaNode.isDirty();
- }
+ boolean isDirty();
- public void addChildAt(ReactShadowNode child, int i) {
- if (child.mParent != null) {
- throw new IllegalViewOperationException(
- "Tried to add child that already has a parent! Remove it from its parent first.");
- }
- if (mChildren == null) {
- mChildren = new ArrayList(4);
- }
- mChildren.add(i, child);
- child.mParent = this;
+ void addChildAt(T child, int i);
- // If a CSS node has measure defined, the layout algorithm will not visit its children. Even
- // more, it asserts that you don't add children to nodes with measure functions.
- if (mYogaNode != null && !isYogaLeafNode()) {
- YogaNode childYogaNode = child.mYogaNode;
- if (childYogaNode == null) {
- throw new RuntimeException(
- "Cannot add a child that doesn't have a YogaNode to a parent without a measure " +
- "function! (Trying to add a '" + child.getClass().getSimpleName() + "' to a '" +
- getClass().getSimpleName() + "')");
- }
- mYogaNode.addChildAt(childYogaNode, i);
- }
- markUpdated();
+ T removeChildAt(int i);
- int increase = child.mIsLayoutOnly ? child.mTotalNativeChildren : 1;
- mTotalNativeChildren += increase;
+ int getChildCount();
- updateNativeChildrenCountInParent(increase);
- }
+ T getChildAt(int i);
- public ReactShadowNode removeChildAt(int i) {
- if (mChildren == null) {
- throw new ArrayIndexOutOfBoundsException(
- "Index " + i + " out of bounds: node has no children");
- }
- ReactShadowNode removed = mChildren.remove(i);
- removed.mParent = null;
+ int indexOf(T child);
- if (mYogaNode != null && !isYogaLeafNode()) {
- mYogaNode.removeChildAt(i);
- }
- markUpdated();
-
- int decrease = removed.mIsLayoutOnly ? removed.mTotalNativeChildren : 1;
- mTotalNativeChildren -= decrease;
- updateNativeChildrenCountInParent(-decrease);
- return removed;
- }
-
- public final int getChildCount() {
- return mChildren == null ? 0 : mChildren.size();
- }
-
- public final ReactShadowNode getChildAt(int i) {
- if (mChildren == null) {
- throw new ArrayIndexOutOfBoundsException(
- "Index " + i + " out of bounds: node has no children");
- }
- return mChildren.get(i);
- }
-
- public final int indexOf(ReactShadowNode child) {
- return mChildren == null ? -1 : mChildren.indexOf(child);
- }
-
- public void removeAndDisposeAllChildren() {
- if (getChildCount() == 0) {
- return;
- }
-
- int decrease = 0;
- for (int i = getChildCount() - 1; i >= 0; i--) {
- if (mYogaNode != null && !isYogaLeafNode()) {
- mYogaNode.removeChildAt(i);
- }
- ReactShadowNode toRemove = getChildAt(i);
- toRemove.mParent = null;
- toRemove.dispose();
-
- decrease += toRemove.mIsLayoutOnly ? toRemove.mTotalNativeChildren : 1;
- }
- Assertions.assertNotNull(mChildren).clear();
- markUpdated();
-
- mTotalNativeChildren -= decrease;
- updateNativeChildrenCountInParent(-decrease);
- }
-
- private void updateNativeChildrenCountInParent(int delta) {
- if (mIsLayoutOnly) {
- ReactShadowNode parent = getParent();
- while (parent != null) {
- parent.mTotalNativeChildren += delta;
- if (!parent.mIsLayoutOnly) {
- break;
- }
- parent = parent.getParent();
- }
- }
- }
+ void removeAndDisposeAllChildren();
/**
* This method will be called by {@link UIManagerModule} once per batch, before calculating
- * layout. Will be only called for nodes that are marked as updated with {@link #markUpdated()}
- * or require layouting (marked with {@link #dirty()}).
+ * layout. Will be only called for nodes that are marked as updated with {@link #markUpdated()} or
+ * require layouting (marked with {@link #dirty()}).
*/
- public void onBeforeLayout() {
- }
+ void onBeforeLayout();
- public final void updateProperties(ReactStylesDiffMap props) {
- ViewManagerPropertyUpdater.updateProps(this, props);
- onAfterUpdateTransaction();
- }
+ void updateProperties(ReactStylesDiffMap props);
- public void onAfterUpdateTransaction() {
- // no-op
- }
+ void onAfterUpdateTransaction();
/**
* Called after layout step at the end of the UI batch from {@link UIManagerModule}. May be used
- * to enqueue additional ui operations for the native view. Will only be called on nodes marked
- * as updated either with {@link #dirty()} or {@link #markUpdated()}.
+ * to enqueue additional ui operations for the native view. Will only be called on nodes marked as
+ * updated either with {@link #dirty()} or {@link #markUpdated()}.
*
* @param uiViewOperationQueue interface for enqueueing UI operations
*/
- public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
- }
+ void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue);
- /**
- * @return true if layout (position or dimensions) changed, false otherwise.
- */
+ /** @return true if layout (position or dimensions) changed, false otherwise. */
/* package */ boolean dispatchUpdates(
float absoluteX,
float absoluteY,
UIViewOperationQueue uiViewOperationQueue,
- NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
- if (mNodeUpdated) {
- onCollectExtraUpdates(uiViewOperationQueue);
- }
+ NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer);
- if (hasNewLayout()) {
- float layoutX = getLayoutX();
- float layoutY = getLayoutY();
- int newAbsoluteLeft = Math.round(absoluteX + layoutX);
- int newAbsoluteTop = Math.round(absoluteY + layoutY);
- int newAbsoluteRight = Math.round(absoluteX + layoutX + getLayoutWidth());
- int newAbsoluteBottom = Math.round(absoluteY + layoutY + getLayoutHeight());
+ int getReactTag();
- int newScreenX = Math.round(layoutX);
- int newScreenY = Math.round(layoutY);
- int newScreenWidth = newAbsoluteRight - newAbsoluteLeft;
- int newScreenHeight = newAbsoluteBottom - newAbsoluteTop;
+ void setReactTag(int reactTag);
- boolean layoutHasChanged =
- newScreenX != mScreenX ||
- newScreenY != mScreenY ||
- newScreenWidth != mScreenWidth ||
- newScreenHeight != mScreenHeight;
+ T getRootNode();
- mScreenX = newScreenX;
- mScreenY = newScreenY;
- mScreenWidth = newScreenWidth;
- mScreenHeight = newScreenHeight;
+ void setRootNode(T rootNode);
- if (layoutHasChanged) {
- nativeViewHierarchyOptimizer.handleUpdateLayout(this);
- }
+ void setViewClassName(String viewClassName);
- return layoutHasChanged;
- } else {
- return false;
- }
- }
-
- public final int getReactTag() {
- return mReactTag;
- }
-
- public void setReactTag(int reactTag) {
- mReactTag = reactTag;
- }
-
- public final ReactShadowNode getRootNode() {
- return Assertions.assertNotNull(mRootNode);
- }
-
- /* package */ final void setRootNode(ReactShadowNode rootNode) {
- mRootNode = rootNode;
- }
-
- /* package */ final void setViewClassName(String viewClassName) {
- mViewClassName = viewClassName;
- }
-
- public final @Nullable ReactShadowNode getParent() {
- return mParent;
- }
+ @Nullable
+ T getParent();
/**
* Get the {@link ThemedReactContext} associated with this {@link ReactShadowNode}. This will
* never change during the lifetime of a {@link ReactShadowNode} instance, but different instances
* can have different contexts; don't cache any calculations based on theme values globally.
*/
- public final ThemedReactContext getThemedContext() {
- return Assertions.assertNotNull(mThemedContext);
- }
+ ThemedReactContext getThemedContext();
- public void setThemedContext(ThemedReactContext themedContext) {
- mThemedContext = themedContext;
- }
+ void setThemedContext(ThemedReactContext themedContext);
- public final boolean shouldNotifyOnLayout() {
- return mShouldNotifyOnLayout;
- }
+ boolean shouldNotifyOnLayout();
- public void calculateLayout() {
- mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
- }
+ void calculateLayout();
- public final boolean hasNewLayout() {
- return mYogaNode != null && mYogaNode.hasNewLayout();
- }
+ boolean hasNewLayout();
- public final void markLayoutSeen() {
- if (mYogaNode != null) {
- mYogaNode.markLayoutSeen();
- }
- }
+ void markLayoutSeen();
/**
* Adds a child that the native view hierarchy will have at this index in the native view
* corresponding to this node.
*/
- public final void addNativeChildAt(ReactShadowNode child, int nativeIndex) {
- Assertions.assertCondition(!mIsLayoutOnly);
- Assertions.assertCondition(!child.mIsLayoutOnly);
+ void addNativeChildAt(T child, int nativeIndex);
- if (mNativeChildren == null) {
- mNativeChildren = new ArrayList<>(4);
- }
+ T removeNativeChildAt(int i);
- mNativeChildren.add(nativeIndex, child);
- child.mNativeParent = this;
- }
+ void removeAllNativeChildren();
- public final ReactShadowNode removeNativeChildAt(int i) {
- Assertions.assertNotNull(mNativeChildren);
- ReactShadowNode removed = mNativeChildren.remove(i);
- removed.mNativeParent = null;
- return removed;
- }
+ int getNativeChildCount();
- public final void removeAllNativeChildren() {
- if (mNativeChildren != null) {
- for (int i = mNativeChildren.size() - 1; i >= 0; i--) {
- mNativeChildren.get(i).mNativeParent = null;
- }
- mNativeChildren.clear();
- }
- }
+ int indexOfNativeChild(T nativeChild);
- public final int getNativeChildCount() {
- return mNativeChildren == null ? 0 : mNativeChildren.size();
- }
-
- public final int indexOfNativeChild(ReactShadowNode nativeChild) {
- Assertions.assertNotNull(mNativeChildren);
- return mNativeChildren.indexOf(nativeChild);
- }
-
- public final @Nullable ReactShadowNode getNativeParent() {
- return mNativeParent;
- }
+ @Nullable
+ T getNativeParent();
/**
- * Sets whether this node only contributes to the layout of its children without doing any
- * drawing or functionality itself.
+ * Sets whether this node only contributes to the layout of its children without doing any drawing
+ * or functionality itself.
*/
- public final void setIsLayoutOnly(boolean isLayoutOnly) {
- Assertions.assertCondition(getParent() == null, "Must remove from no opt parent first");
- Assertions.assertCondition(mNativeParent == null, "Must remove from native parent first");
- Assertions.assertCondition(getNativeChildCount() == 0, "Must remove all native children first");
- mIsLayoutOnly = isLayoutOnly;
- }
+ void setIsLayoutOnly(boolean isLayoutOnly);
- public final boolean isLayoutOnly() {
- return mIsLayoutOnly;
- }
+ boolean isLayoutOnly();
- public final int getTotalNativeChildren() {
- return mTotalNativeChildren;
- }
+ int getTotalNativeChildren();
- public boolean isDescendantOf(ReactShadowNode ancestorNode) {
- ReactShadowNode parentNode = getParent();
-
- boolean isDescendant = false;
-
- while (parentNode != null) {
- if (parentNode == ancestorNode) {
- isDescendant = true;
- break;
- } else {
- parentNode = parentNode.getParent();
- }
- }
-
- return isDescendant;
- }
+ boolean isDescendantOf(T ancestorNode);
/**
* Returns the offset within the native children owned by all layout-only nodes in the subtree
@@ -488,368 +192,143 @@ public class ReactShadowNode {
* in this subtree (which means that the given child will be a sibling of theirs in the final
* native hierarchy since they'll get attached to the same native parent).
*
- * Basically, a view might have children that have been optimized away by
- * {@link NativeViewHierarchyOptimizer}. Since those children will then add their native children
- * to this view, we now have ranges of native children that correspond to single unoptimized
- * children. The purpose of this method is to return the index within the native children that
- * corresponds to the **start** of the native children that belong to the given child. Also, note
- * that all of the children of a view might be optimized away, so this could return the same value
- * for multiple different children.
+ * Basically, a view might have children that have been optimized away by {@link
+ * NativeViewHierarchyOptimizer}. Since those children will then add their native children to this
+ * view, we now have ranges of native children that correspond to single unoptimized children. The
+ * purpose of this method is to return the index within the native children that corresponds to
+ * the **start** of the native children that belong to the given child. Also, note that all of the
+ * children of a view might be optimized away, so this could return the same value for multiple
+ * different children.
*
- * Example. Native children are represented by (N) where N is the no-opt child they came from. If
- * no children are optimized away it'd look like this: (0) (1) (2) (3) ... (n)
+ *
Example. Native children are represented by (N) where N is the no-opt child they came from.
+ * If no children are optimized away it'd look like this: (0) (1) (2) (3) ... (n)
*
- * In case some children are optimized away, it might look like this:
- * (0) (1) (1) (1) (3) (3) (4)
+ *
In case some children are optimized away, it might look like this: (0) (1) (1) (1) (3) (3)
+ * (4)
*
- * In that case:
- * getNativeOffsetForChild(Node 0) => 0
- * getNativeOffsetForChild(Node 1) => 1
- * getNativeOffsetForChild(Node 2) => 4
- * getNativeOffsetForChild(Node 3) => 4
+ *
In that case: getNativeOffsetForChild(Node 0) => 0 getNativeOffsetForChild(Node 1) => 1
+ * getNativeOffsetForChild(Node 2) => 4 getNativeOffsetForChild(Node 3) => 4
* getNativeOffsetForChild(Node 4) => 6
*/
- public final int getNativeOffsetForChild(ReactShadowNode child) {
- int index = 0;
- boolean found = false;
- for (int i = 0; i < getChildCount(); i++) {
- ReactShadowNode current = getChildAt(i);
- if (child == current) {
- found = true;
- break;
- }
- index += (current.mIsLayoutOnly ? current.getTotalNativeChildren() : 1);
- }
- if (!found) {
- throw new RuntimeException("Child " + child.mReactTag + " was not a child of " + mReactTag);
- }
- return index;
- }
-
- public final float getLayoutX() {
- return mYogaNode.getLayoutX();
- }
-
- public final float getLayoutY() {
- return mYogaNode.getLayoutY();
- }
-
- public final float getLayoutWidth() {
- return mYogaNode.getLayoutWidth();
- }
-
- public final float getLayoutHeight() {
- return mYogaNode.getLayoutHeight();
- }
-
- /**
- * @return the x position of the corresponding view on the screen, rounded to pixels
- */
- public int getScreenX() {
- return mScreenX;
- }
-
- /**
- * @return the y position of the corresponding view on the screen, rounded to pixels
- */
- public int getScreenY() {
- return mScreenY;
- }
-
- /**
- * @return width corrected for rounding to pixels.
- */
- public int getScreenWidth() {
- return mScreenWidth;
- }
-
- /**
- * @return height corrected for rounding to pixels.
- */
- public int getScreenHeight() {
- return mScreenHeight;
- }
-
- public final YogaDirection getLayoutDirection() {
- return mYogaNode.getLayoutDirection();
- }
-
- public void setLayoutDirection(YogaDirection direction) {
- mYogaNode.setDirection(direction);
- }
-
- public final YogaValue getStyleWidth() {
- return mYogaNode.getWidth();
- }
-
- public void setStyleWidth(float widthPx) {
- mYogaNode.setWidth(widthPx);
- }
-
- public void setStyleWidthPercent(float percent) {
- mYogaNode.setWidthPercent(percent);
- }
-
- public void setStyleWidthAuto() {
- mYogaNode.setWidthAuto();
- }
-
- public void setStyleMinWidth(float widthPx) {
- mYogaNode.setMinWidth(widthPx);
- }
-
- public void setStyleMinWidthPercent(float percent) {
- mYogaNode.setMinWidthPercent(percent);
- }
-
- public void setStyleMaxWidth(float widthPx) {
- mYogaNode.setMaxWidth(widthPx);
- }
-
- public void setStyleMaxWidthPercent(float percent) {
- mYogaNode.setMaxWidthPercent(percent);
- }
-
- public final YogaValue getStyleHeight() {
- return mYogaNode.getHeight();
- }
-
- public void setStyleHeight(float heightPx) {
- mYogaNode.setHeight(heightPx);
- }
-
- public void setStyleHeightPercent(float percent) {
- mYogaNode.setHeightPercent(percent);
- }
-
- public void setStyleHeightAuto() {
- mYogaNode.setHeightAuto();
- }
-
- public void setStyleMinHeight(float widthPx) {
- mYogaNode.setMinHeight(widthPx);
- }
-
- public void setStyleMinHeightPercent(float percent) {
- mYogaNode.setMinHeightPercent(percent);
- }
-
- public void setStyleMaxHeight(float widthPx) {
- mYogaNode.setMaxHeight(widthPx);
- }
-
- public void setStyleMaxHeightPercent(float percent) {
- mYogaNode.setMaxHeightPercent(percent);
- }
-
- public void setFlex(float flex) {
- mYogaNode.setFlex(flex);
- }
-
- public void setFlexGrow(float flexGrow) {
- mYogaNode.setFlexGrow(flexGrow);
- }
-
- public void setFlexShrink(float flexShrink) {
- mYogaNode.setFlexShrink(flexShrink);
- }
-
- public void setFlexBasis(float flexBasis) {
- mYogaNode.setFlexBasis(flexBasis);
- }
-
- public void setFlexBasisAuto() {
- mYogaNode.setFlexBasisAuto();
- }
-
- public void setFlexBasisPercent(float percent) {
- mYogaNode.setFlexBasisPercent(percent);
- }
-
- public void setStyleAspectRatio(float aspectRatio) {
- mYogaNode.setAspectRatio(aspectRatio);
- }
-
- public void setFlexDirection(YogaFlexDirection flexDirection) {
- mYogaNode.setFlexDirection(flexDirection);
- }
-
- public void setFlexWrap(YogaWrap wrap) {
- mYogaNode.setWrap(wrap);
- }
-
- public void setAlignSelf(YogaAlign alignSelf) {
- mYogaNode.setAlignSelf(alignSelf);
- }
-
- public void setAlignItems(YogaAlign alignItems) {
- mYogaNode.setAlignItems(alignItems);
- }
-
- public void setAlignContent(YogaAlign alignContent) {
- mYogaNode.setAlignContent(alignContent);
- }
-
- public void setJustifyContent(YogaJustify justifyContent) {
- mYogaNode.setJustifyContent(justifyContent);
- }
-
- public void setOverflow(YogaOverflow overflow) {
- mYogaNode.setOverflow(overflow);
- }
-
- public void setDisplay(YogaDisplay display) {
- mYogaNode.setDisplay(display);
- }
-
- public void setMargin(int spacingType, float margin) {
- mYogaNode.setMargin(YogaEdge.fromInt(spacingType), margin);
- }
-
- public void setMarginPercent(int spacingType, float percent) {
- mYogaNode.setMarginPercent(YogaEdge.fromInt(spacingType), percent);
- }
-
- public void setMarginAuto(int spacingType) {
- mYogaNode.setMarginAuto(YogaEdge.fromInt(spacingType));
- }
-
- public final float getPadding(int spacingType) {
- return mYogaNode.getLayoutPadding(YogaEdge.fromInt(spacingType));
- }
-
- public final YogaValue getStylePadding(int spacingType) {
- return mYogaNode.getPadding(YogaEdge.fromInt(spacingType));
- }
-
- public void setDefaultPadding(int spacingType, float padding) {
- mDefaultPadding.set(spacingType, padding);
- updatePadding();
- }
-
- public void setPadding(int spacingType, float padding) {
- mPadding[spacingType] = padding;
- mPaddingIsPercent[spacingType] = false;
- updatePadding();
- }
-
- public void setPaddingPercent(int spacingType, float percent) {
- mPadding[spacingType] = percent;
- mPaddingIsPercent[spacingType] = !YogaConstants.isUndefined(percent);
- updatePadding();
- }
-
- private void updatePadding() {
- for (int spacingType = Spacing.LEFT; spacingType <= Spacing.ALL; spacingType++) {
- if (spacingType == Spacing.LEFT ||
- spacingType == Spacing.RIGHT ||
- spacingType == Spacing.START ||
- spacingType == Spacing.END) {
- if (YogaConstants.isUndefined(mPadding[spacingType]) &&
- YogaConstants.isUndefined(mPadding[Spacing.HORIZONTAL]) &&
- YogaConstants.isUndefined(mPadding[Spacing.ALL])) {
- mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
- continue;
- }
- } else if (spacingType == Spacing.TOP || spacingType == Spacing.BOTTOM) {
- if (YogaConstants.isUndefined(mPadding[spacingType]) &&
- YogaConstants.isUndefined(mPadding[Spacing.VERTICAL]) &&
- YogaConstants.isUndefined(mPadding[Spacing.ALL])) {
- mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
- continue;
- }
- } else {
- if (YogaConstants.isUndefined(mPadding[spacingType])) {
- mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
- continue;
- }
- }
-
- if (mPaddingIsPercent[spacingType]) {
- mYogaNode.setPaddingPercent(YogaEdge.fromInt(spacingType), mPadding[spacingType]);
- } else {
- mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mPadding[spacingType]);
- }
- }
- }
-
- public void setBorder(int spacingType, float borderWidth) {
- mYogaNode.setBorder(YogaEdge.fromInt(spacingType), borderWidth);
- }
-
- public void setPosition(int spacingType, float position) {
- mYogaNode.setPosition(YogaEdge.fromInt(spacingType), position);
- }
-
- public void setPositionPercent(int spacingType, float percent) {
- mYogaNode.setPositionPercent(YogaEdge.fromInt(spacingType), percent);
- }
-
- public void setPositionType(YogaPositionType positionType) {
- mYogaNode.setPositionType(positionType);
- }
-
- public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
- mShouldNotifyOnLayout = shouldNotifyOnLayout;
- }
-
- public void setBaselineFunction(YogaBaselineFunction baselineFunction) {
- mYogaNode.setBaselineFunction(baselineFunction);
- }
-
- public void setMeasureFunction(YogaMeasureFunction measureFunction) {
- if ((measureFunction == null ^ mYogaNode.isMeasureDefined()) &&
- getChildCount() != 0) {
- throw new RuntimeException(
- "Since a node with a measure function does not add any native yoga children, it's " +
- "not safe to transition to/from having a measure function unless a node has no children");
- }
- mYogaNode.setMeasureFunction(measureFunction);
- }
-
- public boolean isMeasureDefined() {
- return mYogaNode.isMeasureDefined();
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toStringWithIndentation(sb, 0);
- return sb.toString();
- }
-
- private void toStringWithIndentation(StringBuilder result, int level) {
- // Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
- for (int i = 0; i < level; ++i) {
- result.append("__");
- }
-
- result
- .append(getClass().getSimpleName())
- .append(" ");
- if (mYogaNode != null) {
- result
- .append(getLayoutWidth())
- .append(",")
- .append(getLayoutHeight());
- } else {
- result.append("(virtual node)");
- }
- result.append("\n");
-
- if (getChildCount() == 0) {
- return;
- }
-
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).toStringWithIndentation(result, level + 1);
- }
- }
-
- public void dispose() {
- if (mYogaNode != null) {
- mYogaNode.reset();
- YogaNodePool.get().release(mYogaNode);
- }
- }
+ int getNativeOffsetForChild(T child);
+
+ float getLayoutX();
+
+ float getLayoutY();
+
+ float getLayoutWidth();
+
+ float getLayoutHeight();
+
+ /** @return the x position of the corresponding view on the screen, rounded to pixels */
+ int getScreenX();
+
+ /** @return the y position of the corresponding view on the screen, rounded to pixels */
+ int getScreenY();
+
+ /** @return width corrected for rounding to pixels. */
+ int getScreenWidth();
+
+ /** @return height corrected for rounding to pixels. */
+ int getScreenHeight();
+
+ YogaDirection getLayoutDirection();
+
+ void setLayoutDirection(YogaDirection direction);
+
+ YogaValue getStyleWidth();
+
+ void setStyleWidth(float widthPx);
+
+ void setStyleWidthPercent(float percent);
+
+ void setStyleWidthAuto();
+
+ void setStyleMinWidth(float widthPx);
+
+ void setStyleMinWidthPercent(float percent);
+
+ void setStyleMaxWidth(float widthPx);
+
+ void setStyleMaxWidthPercent(float percent);
+
+ YogaValue getStyleHeight();
+
+ void setStyleHeight(float heightPx);
+
+ void setStyleHeightPercent(float percent);
+
+ void setStyleHeightAuto();
+
+ void setStyleMinHeight(float widthPx);
+
+ void setStyleMinHeightPercent(float percent);
+
+ void setStyleMaxHeight(float widthPx);
+
+ void setStyleMaxHeightPercent(float percent);
+
+ void setFlex(float flex);
+
+ void setFlexGrow(float flexGrow);
+
+ void setFlexShrink(float flexShrink);
+
+ void setFlexBasis(float flexBasis);
+
+ void setFlexBasisAuto();
+
+ void setFlexBasisPercent(float percent);
+
+ void setStyleAspectRatio(float aspectRatio);
+
+ void setFlexDirection(YogaFlexDirection flexDirection);
+
+ void setFlexWrap(YogaWrap wrap);
+
+ void setAlignSelf(YogaAlign alignSelf);
+
+ void setAlignItems(YogaAlign alignItems);
+
+ void setAlignContent(YogaAlign alignContent);
+
+ void setJustifyContent(YogaJustify justifyContent);
+
+ void setOverflow(YogaOverflow overflow);
+
+ void setDisplay(YogaDisplay display);
+
+ void setMargin(int spacingType, float margin);
+
+ void setMarginPercent(int spacingType, float percent);
+
+ void setMarginAuto(int spacingType);
+
+ float getPadding(int spacingType);
+
+ YogaValue getStylePadding(int spacingType);
+
+ void setDefaultPadding(int spacingType, float padding);
+
+ void setPadding(int spacingType, float padding);
+
+ void setPaddingPercent(int spacingType, float percent);
+
+ void setBorder(int spacingType, float borderWidth);
+
+ void setPosition(int spacingType, float position);
+
+ void setPositionPercent(int spacingType, float percent);
+
+ void setPositionType(YogaPositionType positionType);
+
+ void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout);
+
+ void setBaselineFunction(YogaBaselineFunction baselineFunction);
+
+ void setMeasureFunction(YogaMeasureFunction measureFunction);
+
+ boolean isMeasureDefined();
+
+ void dispose();
}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java
new file mode 100644
index 000000000..1ec64ec60
--- /dev/null
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java
@@ -0,0 +1,940 @@
+/**
+ * Copyright (c) 2015-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.
+ */
+
+package com.facebook.react.uimanager;
+
+import com.facebook.infer.annotation.Assertions;
+import com.facebook.yoga.YogaAlign;
+import com.facebook.yoga.YogaBaselineFunction;
+import com.facebook.yoga.YogaConfig;
+import com.facebook.yoga.YogaConstants;
+import com.facebook.yoga.YogaDirection;
+import com.facebook.yoga.YogaDisplay;
+import com.facebook.yoga.YogaEdge;
+import com.facebook.yoga.YogaFlexDirection;
+import com.facebook.yoga.YogaJustify;
+import com.facebook.yoga.YogaMeasureFunction;
+import com.facebook.yoga.YogaNode;
+import com.facebook.yoga.YogaOverflow;
+import com.facebook.yoga.YogaPositionType;
+import com.facebook.yoga.YogaValue;
+import com.facebook.yoga.YogaWrap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import javax.annotation.Nullable;
+
+/**
+ * Base node class for representing virtual tree of React nodes. Shadow nodes are used primarily for
+ * layouting therefore it extends {@link YogaNode} to allow that. They also help with handling
+ * Common base subclass of {@link YogaNode} for all layout nodes for react-based view. It extends
+ * {@link YogaNode} by adding additional capabilities.
+ *
+ *
Instances of this class receive property updates from JS via @{link UIManagerModule}.
+ * Subclasses may use {@link #updateShadowNode} to persist some of the updated fields in the node
+ * instance that corresponds to a particular view type.
+ *
+ *
Subclasses of {@link ReactShadowNodeImpl} should be created only from {@link ViewManager} that
+ * corresponds to a certain type of native view. They will be updated and accessed only from JS
+ * thread. Subclasses of {@link ViewManager} may choose to use base class {@link
+ * ReactShadowNodeImpl} or custom subclass of it if necessary.
+ *
+ *
The primary use-case for {@link ReactShadowNodeImpl} nodes is to calculate layouting. Although
+ * this might be extended. For some examples please refer to ARTGroupYogaNode or ReactTextYogaNode.
+ *
+ *
This class allows for the native view hierarchy to not be an exact copy of the hierarchy
+ * received from JS by keeping track of both JS children (e.g. {@link #getChildCount()} and
+ * separately native children (e.g. {@link #getNativeChildCount()}). See {@link
+ * NativeViewHierarchyOptimizer} for more information.
+ */
+public class ReactShadowNodeImpl implements ReactShadowNode {
+
+ private int mReactTag;
+ private @Nullable String mViewClassName;
+ private @Nullable ReactShadowNodeImpl mRootNode;
+ private @Nullable ThemedReactContext mThemedContext;
+ private boolean mShouldNotifyOnLayout;
+ private boolean mNodeUpdated = true;
+ private @Nullable ArrayList mChildren;
+ private @Nullable ReactShadowNodeImpl mParent;
+
+ // layout-only nodes
+ private boolean mIsLayoutOnly;
+ private int mTotalNativeChildren = 0;
+ private @Nullable ReactShadowNodeImpl mNativeParent;
+ private @Nullable ArrayList mNativeChildren;
+ private int mScreenX;
+ private int mScreenY;
+ private int mScreenWidth;
+ private int mScreenHeight;
+ private final Spacing mDefaultPadding = new Spacing(0);
+ private final float[] mPadding = new float[Spacing.ALL + 1];
+ private final boolean[] mPaddingIsPercent = new boolean[Spacing.ALL + 1];
+ private final YogaNode mYogaNode;
+ private static YogaConfig sYogaConfig;
+
+ public ReactShadowNodeImpl() {
+ if (!isVirtual()) {
+ YogaNode node = YogaNodePool.get().acquire();
+ if (sYogaConfig == null) {
+ sYogaConfig = new YogaConfig();
+ sYogaConfig.setPointScaleFactor(0f);
+ sYogaConfig.setUseLegacyStretchBehaviour(true);
+ }
+ if (node == null) {
+ node = new YogaNode(sYogaConfig);
+ }
+ mYogaNode = node;
+ Arrays.fill(mPadding, YogaConstants.UNDEFINED);
+ } else {
+ mYogaNode = null;
+ }
+ }
+
+ /**
+ * Nodes that return {@code true} will be treated as "virtual" nodes. That is, nodes that are not
+ * mapped into native views (e.g. nested text node). By default this method returns {@code false}.
+ */
+ @Override
+ public boolean isVirtual() {
+ return false;
+ }
+
+ /**
+ * Nodes that return {@code true} will be treated as a root view for the virtual nodes tree. It
+ * means that {@link NativeViewHierarchyManager} will not try to perform {@code manageChildren}
+ * operation on such views. Good example is {@code InputText} view that may have children {@code
+ * Text} nodes but this whole hierarchy will be mapped to a single android {@link EditText} view.
+ */
+ @Override
+ public boolean isVirtualAnchor() {
+ return false;
+ }
+
+ /**
+ * Nodes that return {@code true} will not manage (and and remove) child Yoga nodes. For example
+ * {@link ReactTextInputShadowNode} or {@link ReactTextShadowNode} have child nodes, which do not
+ * want Yoga to lay out, so in the eyes of Yoga it is a leaf node. Override this method in
+ * subclass to enforce this requirement.
+ */
+ @Override
+ public boolean isYogaLeafNode() {
+ return isMeasureDefined();
+ }
+
+ @Override
+ public final String getViewClass() {
+ return Assertions.assertNotNull(mViewClassName);
+ }
+
+ @Override
+ public final boolean hasUpdates() {
+ return mNodeUpdated || hasNewLayout() || isDirty();
+ }
+
+ @Override
+ public final void markUpdateSeen() {
+ mNodeUpdated = false;
+ if (hasNewLayout()) {
+ markLayoutSeen();
+ }
+ }
+
+ @Override
+ public void markUpdated() {
+ if (mNodeUpdated) {
+ return;
+ }
+ mNodeUpdated = true;
+ ReactShadowNodeImpl parent = getParent();
+ if (parent != null) {
+ parent.markUpdated();
+ }
+ }
+
+ @Override
+ public final boolean hasUnseenUpdates() {
+ return mNodeUpdated;
+ }
+
+ @Override
+ public void dirty() {
+ if (!isVirtual()) {
+ mYogaNode.dirty();
+ }
+ }
+
+ @Override
+ public final boolean isDirty() {
+ return mYogaNode != null && mYogaNode.isDirty();
+ }
+
+ @Override
+ public void addChildAt(ReactShadowNodeImpl child, int i) {
+ if (child.getParent() != null) {
+ throw new IllegalViewOperationException(
+ "Tried to add child that already has a parent! Remove it from its parent first.");
+ }
+ if (mChildren == null) {
+ mChildren = new ArrayList(4);
+ }
+ mChildren.add(i, child);
+ child.mParent = this;
+
+ // If a CSS node has measure defined, the layout algorithm will not visit its children. Even
+ // more, it asserts that you don't add children to nodes with measure functions.
+ if (mYogaNode != null && !isYogaLeafNode()) {
+ YogaNode childYogaNode = child.mYogaNode;
+ if (childYogaNode == null) {
+ throw new RuntimeException(
+ "Cannot add a child that doesn't have a YogaNode to a parent without a measure " +
+ "function! (Trying to add a '" + child.getClass().getSimpleName() + "' to a '" +
+ getClass().getSimpleName() + "')");
+ }
+ mYogaNode.addChildAt(childYogaNode, i);
+ }
+ markUpdated();
+
+ int increase = child.isLayoutOnly() ? child.getTotalNativeChildren() : 1;
+ mTotalNativeChildren += increase;
+
+ updateNativeChildrenCountInParent(increase);
+ }
+
+ @Override
+ public ReactShadowNodeImpl removeChildAt(int i) {
+ if (mChildren == null) {
+ throw new ArrayIndexOutOfBoundsException(
+ "Index " + i + " out of bounds: node has no children");
+ }
+ ReactShadowNodeImpl removed = mChildren.remove(i);
+ removed.mParent = null;
+
+ if (mYogaNode != null && !isYogaLeafNode()) {
+ mYogaNode.removeChildAt(i);
+ }
+ markUpdated();
+
+ int decrease = removed.isLayoutOnly() ? removed.getTotalNativeChildren() : 1;
+ mTotalNativeChildren -= decrease;
+ updateNativeChildrenCountInParent(-decrease);
+ return removed;
+ }
+
+ @Override
+ public final int getChildCount() {
+ return mChildren == null ? 0 : mChildren.size();
+ }
+
+ @Override
+ public final ReactShadowNodeImpl getChildAt(int i) {
+ if (mChildren == null) {
+ throw new ArrayIndexOutOfBoundsException(
+ "Index " + i + " out of bounds: node has no children");
+ }
+ return mChildren.get(i);
+ }
+
+ @Override
+ public final int indexOf(ReactShadowNodeImpl child) {
+ return mChildren == null ? -1 : mChildren.indexOf(child);
+ }
+
+ @Override
+ public void removeAndDisposeAllChildren() {
+ if (getChildCount() == 0) {
+ return;
+ }
+
+ int decrease = 0;
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ if (mYogaNode != null && !isYogaLeafNode()) {
+ mYogaNode.removeChildAt(i);
+ }
+ ReactShadowNodeImpl toRemove = getChildAt(i);
+ toRemove.mParent = null;
+ toRemove.dispose();
+
+ decrease += toRemove.isLayoutOnly() ? toRemove.getTotalNativeChildren() : 1;
+ }
+ Assertions.assertNotNull(mChildren).clear();
+ markUpdated();
+
+ mTotalNativeChildren -= decrease;
+ updateNativeChildrenCountInParent(-decrease);
+ }
+
+ private void updateNativeChildrenCountInParent(int delta) {
+ if (mIsLayoutOnly) {
+ ReactShadowNodeImpl parent = getParent();
+ while (parent != null) {
+ parent.mTotalNativeChildren += delta;
+ if (!parent.isLayoutOnly()) {
+ break;
+ }
+ parent = parent.getParent();
+ }
+ }
+ }
+
+ /**
+ * This method will be called by {@link UIManagerModule} once per batch, before calculating
+ * layout. Will be only called for nodes that are marked as updated with {@link #markUpdated()} or
+ * require layouting (marked with {@link #dirty()}).
+ */
+ @Override
+ public void onBeforeLayout() {}
+
+ @Override
+ public final void updateProperties(ReactStylesDiffMap props) {
+ ViewManagerPropertyUpdater.updateProps(this, props);
+ onAfterUpdateTransaction();
+ }
+
+ @Override
+ public void onAfterUpdateTransaction() {
+ // no-op
+ }
+
+ /**
+ * Called after layout step at the end of the UI batch from {@link UIManagerModule}. May be used
+ * to enqueue additional ui operations for the native view. Will only be called on nodes marked as
+ * updated either with {@link #dirty()} or {@link #markUpdated()}.
+ *
+ * @param uiViewOperationQueue interface for enqueueing UI operations
+ */
+ @Override
+ public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {}
+
+ /** @return true if layout (position or dimensions) changed, false otherwise. */
+ @Override
+ public boolean dispatchUpdates(
+ float absoluteX,
+ float absoluteY,
+ UIViewOperationQueue uiViewOperationQueue,
+ NativeViewHierarchyOptimizer nativeViewHierarchyOptimizer) {
+ if (mNodeUpdated) {
+ onCollectExtraUpdates(uiViewOperationQueue);
+ }
+
+ if (hasNewLayout()) {
+ float layoutX = getLayoutX();
+ float layoutY = getLayoutY();
+ int newAbsoluteLeft = Math.round(absoluteX + layoutX);
+ int newAbsoluteTop = Math.round(absoluteY + layoutY);
+ int newAbsoluteRight = Math.round(absoluteX + layoutX + getLayoutWidth());
+ int newAbsoluteBottom = Math.round(absoluteY + layoutY + getLayoutHeight());
+
+ int newScreenX = Math.round(layoutX);
+ int newScreenY = Math.round(layoutY);
+ int newScreenWidth = newAbsoluteRight - newAbsoluteLeft;
+ int newScreenHeight = newAbsoluteBottom - newAbsoluteTop;
+
+ boolean layoutHasChanged =
+ newScreenX != mScreenX ||
+ newScreenY != mScreenY ||
+ newScreenWidth != mScreenWidth ||
+ newScreenHeight != mScreenHeight;
+
+ mScreenX = newScreenX;
+ mScreenY = newScreenY;
+ mScreenWidth = newScreenWidth;
+ mScreenHeight = newScreenHeight;
+
+ if (layoutHasChanged) {
+ nativeViewHierarchyOptimizer.handleUpdateLayout(this);
+ }
+
+ return layoutHasChanged;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public final int getReactTag() {
+ return mReactTag;
+ }
+
+ @Override
+ public void setReactTag(int reactTag) {
+ mReactTag = reactTag;
+ }
+
+ @Override
+ public final ReactShadowNodeImpl getRootNode() {
+ return Assertions.assertNotNull(mRootNode);
+ }
+
+ @Override
+ public final void setRootNode(ReactShadowNodeImpl rootNode) {
+ mRootNode = rootNode;
+ }
+
+ @Override
+ public final void setViewClassName(String viewClassName) {
+ mViewClassName = viewClassName;
+ }
+
+ @Override
+ public final @Nullable ReactShadowNodeImpl getParent() {
+ return mParent;
+ }
+
+ /**
+ * Get the {@link ThemedReactContext} associated with this {@link ReactShadowNodeImpl}. This will
+ * never change during the lifetime of a {@link ReactShadowNodeImpl} instance, but different
+ * instances can have different contexts; don't cache any calculations based on theme values
+ * globally.
+ */
+ @Override
+ public final ThemedReactContext getThemedContext() {
+ return Assertions.assertNotNull(mThemedContext);
+ }
+
+ @Override
+ public void setThemedContext(ThemedReactContext themedContext) {
+ mThemedContext = themedContext;
+ }
+
+ @Override
+ public final boolean shouldNotifyOnLayout() {
+ return mShouldNotifyOnLayout;
+ }
+
+ @Override
+ public void calculateLayout() {
+ mYogaNode.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+ }
+
+ @Override
+ public final boolean hasNewLayout() {
+ return mYogaNode != null && mYogaNode.hasNewLayout();
+ }
+
+ @Override
+ public final void markLayoutSeen() {
+ if (mYogaNode != null) {
+ mYogaNode.markLayoutSeen();
+ }
+ }
+
+ /**
+ * Adds a child that the native view hierarchy will have at this index in the native view
+ * corresponding to this node.
+ */
+ @Override
+ public final void addNativeChildAt(ReactShadowNodeImpl child, int nativeIndex) {
+ Assertions.assertCondition(!mIsLayoutOnly);
+ Assertions.assertCondition(!child.mIsLayoutOnly);
+
+ if (mNativeChildren == null) {
+ mNativeChildren = new ArrayList<>(4);
+ }
+
+ mNativeChildren.add(nativeIndex, child);
+ child.mNativeParent = this;
+ }
+
+ @Override
+ public final ReactShadowNodeImpl removeNativeChildAt(int i) {
+ Assertions.assertNotNull(mNativeChildren);
+ ReactShadowNodeImpl removed = mNativeChildren.remove(i);
+ removed.mNativeParent = null;
+ return removed;
+ }
+
+ @Override
+ public final void removeAllNativeChildren() {
+ if (mNativeChildren != null) {
+ for (int i = mNativeChildren.size() - 1; i >= 0; i--) {
+ mNativeChildren.get(i).mNativeParent = null;
+ }
+ mNativeChildren.clear();
+ }
+ }
+
+ @Override
+ public final int getNativeChildCount() {
+ return mNativeChildren == null ? 0 : mNativeChildren.size();
+ }
+
+ @Override
+ public final int indexOfNativeChild(ReactShadowNodeImpl nativeChild) {
+ Assertions.assertNotNull(mNativeChildren);
+ return mNativeChildren.indexOf(nativeChild);
+ }
+
+ @Override
+ public final @Nullable ReactShadowNodeImpl getNativeParent() {
+ return mNativeParent;
+ }
+
+ /**
+ * Sets whether this node only contributes to the layout of its children without doing any drawing
+ * or functionality itself.
+ */
+ @Override
+ public final void setIsLayoutOnly(boolean isLayoutOnly) {
+ Assertions.assertCondition(getParent() == null, "Must remove from no opt parent first");
+ Assertions.assertCondition(mNativeParent == null, "Must remove from native parent first");
+ Assertions.assertCondition(getNativeChildCount() == 0, "Must remove all native children first");
+ mIsLayoutOnly = isLayoutOnly;
+ }
+
+ @Override
+ public final boolean isLayoutOnly() {
+ return mIsLayoutOnly;
+ }
+
+ @Override
+ public final int getTotalNativeChildren() {
+ return mTotalNativeChildren;
+ }
+
+
+ @Override
+ public boolean isDescendantOf(ReactShadowNodeImpl ancestorNode) {
+ ReactShadowNodeImpl parentNode = getParent();
+
+ boolean isDescendant = false;
+
+ while (parentNode != null) {
+ if (parentNode == ancestorNode) {
+ isDescendant = true;
+ break;
+ } else {
+ parentNode = parentNode.getParent();
+ }
+ }
+
+ return isDescendant;
+ }
+
+ /**
+ * Returns the offset within the native children owned by all layout-only nodes in the subtree
+ * rooted at this node for the given child. Put another way, this returns the number of native
+ * nodes (nodes not optimized out of the native tree) that are a) to the left (visited before by a
+ * DFS) of the given child in the subtree rooted at this node and b) do not have a native parent
+ * in this subtree (which means that the given child will be a sibling of theirs in the final
+ * native hierarchy since they'll get attached to the same native parent).
+ *
+ * Basically, a view might have children that have been optimized away by {@link
+ * NativeViewHierarchyOptimizer}. Since those children will then add their native children to this
+ * view, we now have ranges of native children that correspond to single unoptimized children. The
+ * purpose of this method is to return the index within the native children that corresponds to
+ * the **start** of the native children that belong to the given child. Also, note that all of the
+ * children of a view might be optimized away, so this could return the same value for multiple
+ * different children.
+ *
+ *
Example. Native children are represented by (N) where N is the no-opt child they came from.
+ * If no children are optimized away it'd look like this: (0) (1) (2) (3) ... (n)
+ *
+ *
In case some children are optimized away, it might look like this: (0) (1) (1) (1) (3) (3)
+ * (4)
+ *
+ *
In that case: getNativeOffsetForChild(Node 0) => 0 getNativeOffsetForChild(Node 1) => 1
+ * getNativeOffsetForChild(Node 2) => 4 getNativeOffsetForChild(Node 3) => 4
+ * getNativeOffsetForChild(Node 4) => 6
+ */
+ @Override
+ public final int getNativeOffsetForChild(ReactShadowNodeImpl child) {
+ int index = 0;
+ boolean found = false;
+ for (int i = 0; i < getChildCount(); i++) {
+ ReactShadowNodeImpl current = getChildAt(i);
+ if (child == current) {
+ found = true;
+ break;
+ }
+ index += (current.isLayoutOnly() ? current.getTotalNativeChildren() : 1);
+ }
+ if (!found) {
+ throw new RuntimeException(
+ "Child " + child.getReactTag() + " was not a child of " + mReactTag);
+ }
+ return index;
+ }
+
+ @Override
+ public final float getLayoutX() {
+ return mYogaNode.getLayoutX();
+ }
+
+ @Override
+ public final float getLayoutY() {
+ return mYogaNode.getLayoutY();
+ }
+
+ @Override
+ public final float getLayoutWidth() {
+ return mYogaNode.getLayoutWidth();
+ }
+
+ @Override
+ public final float getLayoutHeight() {
+ return mYogaNode.getLayoutHeight();
+ }
+
+ /** @return the x position of the corresponding view on the screen, rounded to pixels */
+ @Override
+ public int getScreenX() {
+ return mScreenX;
+ }
+
+ /** @return the y position of the corresponding view on the screen, rounded to pixels */
+ @Override
+ public int getScreenY() {
+ return mScreenY;
+ }
+
+ /** @return width corrected for rounding to pixels. */
+ @Override
+ public int getScreenWidth() {
+ return mScreenWidth;
+ }
+
+ /** @return height corrected for rounding to pixels. */
+ @Override
+ public int getScreenHeight() {
+ return mScreenHeight;
+ }
+
+ @Override
+ public final YogaDirection getLayoutDirection() {
+ return mYogaNode.getLayoutDirection();
+ }
+
+ @Override
+ public void setLayoutDirection(YogaDirection direction) {
+ mYogaNode.setDirection(direction);
+ }
+
+ @Override
+ public final YogaValue getStyleWidth() {
+ return mYogaNode.getWidth();
+ }
+
+ @Override
+ public void setStyleWidth(float widthPx) {
+ mYogaNode.setWidth(widthPx);
+ }
+
+ @Override
+ public void setStyleWidthPercent(float percent) {
+ mYogaNode.setWidthPercent(percent);
+ }
+
+ @Override
+ public void setStyleWidthAuto() {
+ mYogaNode.setWidthAuto();
+ }
+
+ @Override
+ public void setStyleMinWidth(float widthPx) {
+ mYogaNode.setMinWidth(widthPx);
+ }
+
+ @Override
+ public void setStyleMinWidthPercent(float percent) {
+ mYogaNode.setMinWidthPercent(percent);
+ }
+
+ @Override
+ public void setStyleMaxWidth(float widthPx) {
+ mYogaNode.setMaxWidth(widthPx);
+ }
+
+ @Override
+ public void setStyleMaxWidthPercent(float percent) {
+ mYogaNode.setMaxWidthPercent(percent);
+ }
+
+ @Override
+ public final YogaValue getStyleHeight() {
+ return mYogaNode.getHeight();
+ }
+
+ @Override
+ public void setStyleHeight(float heightPx) {
+ mYogaNode.setHeight(heightPx);
+ }
+
+ @Override
+ public void setStyleHeightPercent(float percent) {
+ mYogaNode.setHeightPercent(percent);
+ }
+
+ @Override
+ public void setStyleHeightAuto() {
+ mYogaNode.setHeightAuto();
+ }
+
+ @Override
+ public void setStyleMinHeight(float widthPx) {
+ mYogaNode.setMinHeight(widthPx);
+ }
+
+ @Override
+ public void setStyleMinHeightPercent(float percent) {
+ mYogaNode.setMinHeightPercent(percent);
+ }
+
+ @Override
+ public void setStyleMaxHeight(float widthPx) {
+ mYogaNode.setMaxHeight(widthPx);
+ }
+
+ @Override
+ public void setStyleMaxHeightPercent(float percent) {
+ mYogaNode.setMaxHeightPercent(percent);
+ }
+
+ @Override
+ public void setFlex(float flex) {
+ mYogaNode.setFlex(flex);
+ }
+
+ @Override
+ public void setFlexGrow(float flexGrow) {
+ mYogaNode.setFlexGrow(flexGrow);
+ }
+
+ @Override
+ public void setFlexShrink(float flexShrink) {
+ mYogaNode.setFlexShrink(flexShrink);
+ }
+
+ @Override
+ public void setFlexBasis(float flexBasis) {
+ mYogaNode.setFlexBasis(flexBasis);
+ }
+
+ @Override
+ public void setFlexBasisAuto() {
+ mYogaNode.setFlexBasisAuto();
+ }
+
+ @Override
+ public void setFlexBasisPercent(float percent) {
+ mYogaNode.setFlexBasisPercent(percent);
+ }
+
+ @Override
+ public void setStyleAspectRatio(float aspectRatio) {
+ mYogaNode.setAspectRatio(aspectRatio);
+ }
+
+ @Override
+ public void setFlexDirection(YogaFlexDirection flexDirection) {
+ mYogaNode.setFlexDirection(flexDirection);
+ }
+
+ @Override
+ public void setFlexWrap(YogaWrap wrap) {
+ mYogaNode.setWrap(wrap);
+ }
+
+ @Override
+ public void setAlignSelf(YogaAlign alignSelf) {
+ mYogaNode.setAlignSelf(alignSelf);
+ }
+
+ @Override
+ public void setAlignItems(YogaAlign alignItems) {
+ mYogaNode.setAlignItems(alignItems);
+ }
+
+ @Override
+ public void setAlignContent(YogaAlign alignContent) {
+ mYogaNode.setAlignContent(alignContent);
+ }
+
+ @Override
+ public void setJustifyContent(YogaJustify justifyContent) {
+ mYogaNode.setJustifyContent(justifyContent);
+ }
+
+ @Override
+ public void setOverflow(YogaOverflow overflow) {
+ mYogaNode.setOverflow(overflow);
+ }
+
+ @Override
+ public void setDisplay(YogaDisplay display) {
+ mYogaNode.setDisplay(display);
+ }
+
+ @Override
+ public void setMargin(int spacingType, float margin) {
+ mYogaNode.setMargin(YogaEdge.fromInt(spacingType), margin);
+ }
+
+ @Override
+ public void setMarginPercent(int spacingType, float percent) {
+ mYogaNode.setMarginPercent(YogaEdge.fromInt(spacingType), percent);
+ }
+
+ @Override
+ public void setMarginAuto(int spacingType) {
+ mYogaNode.setMarginAuto(YogaEdge.fromInt(spacingType));
+ }
+
+ @Override
+ public final float getPadding(int spacingType) {
+ return mYogaNode.getLayoutPadding(YogaEdge.fromInt(spacingType));
+ }
+
+ @Override
+ public final YogaValue getStylePadding(int spacingType) {
+ return mYogaNode.getPadding(YogaEdge.fromInt(spacingType));
+ }
+
+ @Override
+ public void setDefaultPadding(int spacingType, float padding) {
+ mDefaultPadding.set(spacingType, padding);
+ updatePadding();
+ }
+
+ @Override
+ public void setPadding(int spacingType, float padding) {
+ mPadding[spacingType] = padding;
+ mPaddingIsPercent[spacingType] = false;
+ updatePadding();
+ }
+
+ @Override
+ public void setPaddingPercent(int spacingType, float percent) {
+ mPadding[spacingType] = percent;
+ mPaddingIsPercent[spacingType] = !YogaConstants.isUndefined(percent);
+ updatePadding();
+ }
+
+ private void updatePadding() {
+ for (int spacingType = Spacing.LEFT; spacingType <= Spacing.ALL; spacingType++) {
+ if (spacingType == Spacing.LEFT ||
+ spacingType == Spacing.RIGHT ||
+ spacingType == Spacing.START ||
+ spacingType == Spacing.END) {
+ if (YogaConstants.isUndefined(mPadding[spacingType]) &&
+ YogaConstants.isUndefined(mPadding[Spacing.HORIZONTAL]) &&
+ YogaConstants.isUndefined(mPadding[Spacing.ALL])) {
+ mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
+ continue;
+ }
+ } else if (spacingType == Spacing.TOP || spacingType == Spacing.BOTTOM) {
+ if (YogaConstants.isUndefined(mPadding[spacingType]) &&
+ YogaConstants.isUndefined(mPadding[Spacing.VERTICAL]) &&
+ YogaConstants.isUndefined(mPadding[Spacing.ALL])) {
+ mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
+ continue;
+ }
+ } else {
+ if (YogaConstants.isUndefined(mPadding[spacingType])) {
+ mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mDefaultPadding.getRaw(spacingType));
+ continue;
+ }
+ }
+
+ if (mPaddingIsPercent[spacingType]) {
+ mYogaNode.setPaddingPercent(YogaEdge.fromInt(spacingType), mPadding[spacingType]);
+ } else {
+ mYogaNode.setPadding(YogaEdge.fromInt(spacingType), mPadding[spacingType]);
+ }
+ }
+ }
+
+ @Override
+ public void setBorder(int spacingType, float borderWidth) {
+ mYogaNode.setBorder(YogaEdge.fromInt(spacingType), borderWidth);
+ }
+
+ @Override
+ public void setPosition(int spacingType, float position) {
+ mYogaNode.setPosition(YogaEdge.fromInt(spacingType), position);
+ }
+
+ @Override
+ public void setPositionPercent(int spacingType, float percent) {
+ mYogaNode.setPositionPercent(YogaEdge.fromInt(spacingType), percent);
+ }
+
+ @Override
+ public void setPositionType(YogaPositionType positionType) {
+ mYogaNode.setPositionType(positionType);
+ }
+
+ @Override
+ public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
+ mShouldNotifyOnLayout = shouldNotifyOnLayout;
+ }
+
+ @Override
+ public void setBaselineFunction(YogaBaselineFunction baselineFunction) {
+ mYogaNode.setBaselineFunction(baselineFunction);
+ }
+
+ @Override
+ public void setMeasureFunction(YogaMeasureFunction measureFunction) {
+ if ((measureFunction == null ^ mYogaNode.isMeasureDefined()) &&
+ getChildCount() != 0) {
+ throw new RuntimeException(
+ "Since a node with a measure function does not add any native yoga children, it's " +
+ "not safe to transition to/from having a measure function unless a node has no children");
+ }
+ mYogaNode.setMeasureFunction(measureFunction);
+ }
+
+ @Override
+ public boolean isMeasureDefined() {
+ return mYogaNode.isMeasureDefined();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toStringWithIndentation(sb, 0);
+ return sb.toString();
+ }
+
+ private void toStringWithIndentation(StringBuilder result, int level) {
+ // Spaces and tabs are dropped by IntelliJ logcat integration, so rely on __ instead.
+ for (int i = 0; i < level; ++i) {
+ result.append("__");
+ }
+
+ result
+ .append(getClass().getSimpleName())
+ .append(" ");
+ if (mYogaNode != null) {
+ result
+ .append(getLayoutWidth())
+ .append(",")
+ .append(getLayoutHeight());
+ } else {
+ result.append("(virtual node)");
+ }
+ result.append("\n");
+
+ if (getChildCount() == 0) {
+ return;
+ }
+
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).toStringWithIndentation(result, level + 1);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (mYogaNode != null) {
+ mYogaNode.reset();
+ YogaNodePool.get().release(mYogaNode);
+ }
+ }
+}
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java
index 0a90afd38..2d0cc1fee 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java
@@ -97,7 +97,7 @@ public class UIImplementation {
}
protected ReactShadowNode createRootShadowNode() {
- ReactShadowNode rootCSSNode = new ReactShadowNode();
+ ReactShadowNode rootCSSNode = new ReactShadowNodeImpl();
I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
if (sharedI18nUtilInstance.isRTL(mReactContext)) {
rootCSSNode.setLayoutDirection(YogaDirection.RTL);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
index cfdd34be2..4ef80977a 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java
@@ -2,15 +2,7 @@
package com.facebook.react.uimanager;
-import javax.annotation.Nullable;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
import android.view.View;
-
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
@@ -18,6 +10,11 @@ import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
/**
* This class is responsible for holding view manager property setters and is used in a process of
@@ -333,7 +330,7 @@ import com.facebook.react.uimanager.annotations.ReactPropGroup;
*/
/*package*/ static Map getNativePropSettersForShadowNodeClass(
Class extends ReactShadowNode> cls) {
- if (cls == ReactShadowNode.class) {
+ if (cls == null) {
return EMPTY_PROPS_MAP;
}
Map props = CLASS_PROPS_CACHE.get(cls);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/art/ARTVirtualNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/art/ARTVirtualNode.java
index 70dc6faaf..ac9739b02 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/art/ARTVirtualNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/art/ARTVirtualNode.java
@@ -9,23 +9,21 @@
package com.facebook.react.views.art;
-import javax.annotation.Nullable;
-
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
-
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.uimanager.DisplayMetricsHolder;
+import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.uimanager.ReactShadowNode;
+import javax.annotation.Nullable;
/**
* Base class for ARTView virtual nodes: {@link ARTGroupShadowNode}, {@link ARTShapeShadowNode} and
* indirectly for {@link ARTTextShadowNode}.
*/
-public abstract class ARTVirtualNode extends ReactShadowNode {
+public abstract class ARTVirtualNode extends ReactShadowNodeImpl {
protected static final float MIN_OPACITY_FOR_DRAW = 0.01f;
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostShadowNode.java
index 52f3c2251..8c3b5fde6 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ModalHostShadowNode.java
@@ -10,9 +10,8 @@
package com.facebook.react.views.modal;
import android.graphics.Point;
-
import com.facebook.react.uimanager.LayoutShadowNode;
-import com.facebook.react.uimanager.ReactShadowNode;
+import com.facebook.react.uimanager.ReactShadowNodeImpl;
/**
* We implement the Modal by using an Android Dialog. That will fill the entire window of the
@@ -26,10 +25,10 @@ class ModalHostShadowNode extends LayoutShadowNode {
/**
* We need to set the styleWidth and styleHeight of the one child (represented by the
- * within the in Modal.js. This needs to fill the entire window.
+ * within the in Modal.js. This needs to fill the entire window.
*/
@Override
- public void addChildAt(ReactShadowNode child, int i) {
+ public void addChildAt(ReactShadowNodeImpl child, int i) {
super.addChildAt(child, i);
Point modalSize = ModalHostHelper.getModalHostSize(getThemedContext());
child.setStyleWidth(modalSize.x);
diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactRawTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactRawTextShadowNode.java
index b97a5bf54..6cf0200c7 100644
--- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactRawTextShadowNode.java
+++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactRawTextShadowNode.java
@@ -9,16 +9,15 @@ package com.facebook.react.views.text;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.ReactShadowNode;
+import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
/**
- * {@link ReactShadowNode} class for pure raw text node
- * (aka {@code textContent} in terms of DOM).
- * Raw text node can only have simple string value without any attributes,
- * properties or state.
+ * {@link ReactShadowNode} class for pure raw text node (aka {@code textContent} in terms of DOM).
+ * Raw text node can only have simple string value without any attributes, properties or state.
*/
-public class ReactRawTextShadowNode extends ReactShadowNode {
+public class ReactRawTextShadowNode extends ReactShadowNodeImpl {
@VisibleForTesting public static final String PROP_TEXT = "text";
diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSetterTest.java b/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSetterTest.java
index 169a001db..3022fb19f 100644
--- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSetterTest.java
+++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSetterTest.java
@@ -9,27 +9,25 @@
package com.facebook.react.uimanager;
-import javax.annotation.Nullable;
-
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.JavaOnlyMap;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.uimanager.annotations.ReactPropGroup;
-
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.junit.Rule;
-import org.junit.Test;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.modules.junit4.rule.PowerMockRule;
-import org.robolectric.RobolectricTestRunner;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import com.facebook.react.bridge.JavaOnlyMap;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.facebook.react.uimanager.annotations.ReactPropGroup;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.modules.junit4.rule.PowerMockRule;
+import org.robolectric.RobolectricTestRunner;
+
/**
* Test {@link ReactProp} annotation for {@link ReactShadowNode}. More comprahensive test of this
* annotation can be found in {@link ReactPropAnnotationSetterTest} where we test all possible types
@@ -61,7 +59,7 @@ public class ReactPropForShadowNodeSetterTest {
return new ReactStylesDiffMap(JavaOnlyMap.of(keysAndValues));
}
- private class ShadowViewUnderTest extends ReactShadowNode {
+ private class ShadowViewUnderTest extends ReactShadowNodeImpl {
private ViewManagerUpdatesReceiver mViewManagerUpdatesReceiver;
diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSpecTest.java b/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSpecTest.java
index 1ddcdf047..8b9717dd7 100644
--- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSpecTest.java
+++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropForShadowNodeSpecTest.java
@@ -9,15 +9,12 @@
package com.facebook.react.uimanager;
-import java.util.Map;
-
import android.view.View;
-
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
-
-import org.junit.Test;
+import java.util.Map;
import org.junit.Rule;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.rule.PowerMockRule;
@@ -69,55 +66,61 @@ public class ReactPropForShadowNodeSpecTest {
@Test(expected = RuntimeException.class)
public void testMethodWithWrongNumberOfParams() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactProp(name = "prop")
- public void setterWithIncorrectNumberOfArgs(boolean value, int anotherValue) {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactProp(name = "prop")
+ public void setterWithIncorrectNumberOfArgs(boolean value, int anotherValue) {}
+ }.getClass())
+ .getNativeProps();
}
@Test(expected = RuntimeException.class)
public void testMethodWithTooFewParams() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactProp(name = "prop")
- public void setterWithNoArgs() {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactProp(name = "prop")
+ public void setterWithNoArgs() {}
+ }.getClass())
+ .getNativeProps();
}
@Test(expected = RuntimeException.class)
public void testUnsupportedValueType() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactProp(name = "prop")
- public void setterWithMap(Map value) {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactProp(name = "prop")
+ public void setterWithMap(Map value) {}
+ }.getClass())
+ .getNativeProps();
}
@Test(expected = RuntimeException.class)
public void testGroupInvalidNumberOfParams() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactPropGroup(names = {"prop1", "prop2"})
- public void setterWithTooManyParams(int index, float value, boolean bool) {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactPropGroup(names = {"prop1", "prop2"})
+ public void setterWithTooManyParams(int index, float value, boolean bool) {}
+ }.getClass())
+ .getNativeProps();
}
@Test(expected = RuntimeException.class)
public void testGroupTooFewParams() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactPropGroup(names = {"prop1", "prop2"})
- public void setterWithTooManyParams(int index) {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactPropGroup(names = {"prop1", "prop2"})
+ public void setterWithTooManyParams(int index) {}
+ }.getClass())
+ .getNativeProps();
}
@Test(expected = RuntimeException.class)
public void testGroupNoIndexParam() {
- new BaseViewManager(new ReactShadowNode() {
- @ReactPropGroup(names = {"prop1", "prop2"})
- public void setterWithTooManyParams(float value, boolean bool) {
- }
- }.getClass()).getNativeProps();
+ new BaseViewManager(
+ new ReactShadowNodeImpl() {
+ @ReactPropGroup(names = {"prop1", "prop2"})
+ public void setterWithTooManyParams(float value, boolean bool) {}
+ }.getClass())
+ .getNativeProps();
}
}