Introduce @ReactProp-enabled LayoutShadowNode as a baseclass for most of the shadow nodes.

Differential Revision: D2540232

fb-gh-sync-id: 6dfed70c8253973897f4e447377ec5561862da23
This commit is contained in:
Krzysztof Magiera 2015-10-14 01:27:38 -07:00 committed by facebook-github-bot-9
parent 9493e96e13
commit 589df04846
10 changed files with 197 additions and 201 deletions

View File

@ -1,146 +0,0 @@
/**
* 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 java.util.Locale;
import com.facebook.csslayout.CSSAlign;
import com.facebook.csslayout.CSSConstants;
import com.facebook.csslayout.CSSFlexDirection;
import com.facebook.csslayout.CSSJustify;
import com.facebook.csslayout.CSSNode;
import com.facebook.csslayout.CSSPositionType;
import com.facebook.csslayout.CSSWrap;
import com.facebook.csslayout.Spacing;
/**
* Takes common style properties from JS and applies them to a given {@link CSSNode}.
*/
public class BaseCSSPropertyApplicator {
private static final String PROP_ON_LAYOUT = "onLayout";
/**
* Takes the base props from updateView/manageChildren and applies any CSS styles (if they exist)
* to the given {@link CSSNode}.
*
* TODO(5241893): Add and test border CSS attributes
*/
public static void applyCSSProperties(ReactShadowNode cssNode, CatalystStylesDiffMap props) {
if (props.hasKey(ViewProps.WIDTH)) {
float width = props.getFloat(ViewProps.WIDTH, CSSConstants.UNDEFINED);
cssNode.setStyleWidth(CSSConstants.isUndefined(width) ?
width : PixelUtil.toPixelFromDIP(width));
}
if (props.hasKey(ViewProps.HEIGHT)) {
float height = props.getFloat(ViewProps.HEIGHT, CSSConstants.UNDEFINED);
cssNode.setStyleHeight(CSSConstants.isUndefined(height) ?
height : PixelUtil.toPixelFromDIP(height));
}
if (props.hasKey(ViewProps.LEFT)) {
float left = props.getFloat(ViewProps.LEFT, CSSConstants.UNDEFINED);
cssNode.setPositionLeft(CSSConstants.isUndefined(left) ?
left : PixelUtil.toPixelFromDIP(left));
}
if (props.hasKey(ViewProps.TOP)) {
float top = props.getFloat(ViewProps.TOP, CSSConstants.UNDEFINED);
cssNode.setPositionTop(CSSConstants.isUndefined(top) ?
top : PixelUtil.toPixelFromDIP(top));
}
if (props.hasKey(ViewProps.BOTTOM)) {
float bottom = props.getFloat(ViewProps.BOTTOM, CSSConstants.UNDEFINED);
cssNode.setPositionBottom(CSSConstants.isUndefined(bottom) ?
bottom : PixelUtil.toPixelFromDIP(bottom));
}
if (props.hasKey(ViewProps.RIGHT)) {
float right = props.getFloat(ViewProps.RIGHT, CSSConstants.UNDEFINED);
cssNode.setPositionRight(CSSConstants.isUndefined(right) ?
right : PixelUtil.toPixelFromDIP(right));
}
if (props.hasKey(ViewProps.FLEX)) {
cssNode.setFlex(props.getFloat(ViewProps.FLEX, 0.f));
}
if (props.hasKey(ViewProps.FLEX_DIRECTION)) {
String flexDirectionString = props.getString(ViewProps.FLEX_DIRECTION);
cssNode.setFlexDirection(flexDirectionString == null ?
CSSFlexDirection.COLUMN : CSSFlexDirection.valueOf(
flexDirectionString.toUpperCase(Locale.US)));
}
if (props.hasKey(ViewProps.FLEX_WRAP)) {
String flexWrapString = props.getString(ViewProps.FLEX_WRAP);
cssNode.setWrap(flexWrapString == null ?
CSSWrap.NOWRAP : CSSWrap.valueOf(flexWrapString.toUpperCase(Locale.US)));
}
if (props.hasKey(ViewProps.ALIGN_SELF)) {
String alignSelfString = props.getString(ViewProps.ALIGN_SELF);
cssNode.setAlignSelf(alignSelfString == null ?
CSSAlign.AUTO : CSSAlign.valueOf(
alignSelfString.toUpperCase(Locale.US).replace("-", "_")));
}
if (props.hasKey(ViewProps.ALIGN_ITEMS)) {
String alignItemsString = props.getString(ViewProps.ALIGN_ITEMS);
cssNode.setAlignItems(alignItemsString == null ?
CSSAlign.STRETCH : CSSAlign.valueOf(
alignItemsString.toUpperCase(Locale.US).replace("-", "_")));
}
if (props.hasKey(ViewProps.JUSTIFY_CONTENT)) {
String justifyContentString = props.getString(ViewProps.JUSTIFY_CONTENT);
cssNode.setJustifyContent(justifyContentString == null ? CSSJustify.FLEX_START
: CSSJustify.valueOf(justifyContentString.toUpperCase(Locale.US).replace("-", "_")));
}
for (int i = 0; i < ViewProps.MARGINS.length; i++) {
if (props.hasKey(ViewProps.MARGINS[i])) {
cssNode.setMargin(
ViewProps.PADDING_MARGIN_SPACING_TYPES[i],
PixelUtil.toPixelFromDIP(props.getFloat(ViewProps.MARGINS[i], 0.f)));
}
}
for (int i = 0; i < ViewProps.PADDINGS.length; i++) {
if (props.hasKey(ViewProps.PADDINGS[i])) {
float value = props.getFloat(ViewProps.PADDINGS[i], CSSConstants.UNDEFINED);
cssNode.setPadding(
ViewProps.PADDING_MARGIN_SPACING_TYPES[i],
CSSConstants.isUndefined(value) ? value : PixelUtil.toPixelFromDIP(value));
}
}
for (int i = 0; i < ViewProps.BORDER_WIDTHS.length; i++) {
if (props.hasKey(ViewProps.BORDER_WIDTHS[i])) {
cssNode.setBorder(
ViewProps.BORDER_SPACING_TYPES[i],
PixelUtil.toPixelFromDIP(props.getFloat(ViewProps.BORDER_WIDTHS[i], 0.f)));
}
}
if (props.hasKey(ViewProps.POSITION)) {
String positionString = props.getString(ViewProps.POSITION);
CSSPositionType positionType = positionString == null ?
CSSPositionType.RELATIVE : CSSPositionType.valueOf(positionString.toUpperCase(Locale.US));
cssNode.setPositionType(positionType);
}
if (props.hasKey(PROP_ON_LAYOUT)) {
cssNode.setShouldNotifyOnLayout(props.getBoolean(PROP_ON_LAYOUT, false));
}
}
}

View File

@ -12,7 +12,7 @@ import com.facebook.react.bridge.ReadableMap;
* Base class that should be suitable for the majority of subclasses of {@link ViewManager}.
* It provides support for base view properties such as backgroundColor, opacity, etc.
*/
public abstract class BaseViewManager<T extends View, C extends ReactShadowNode>
public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode>
extends ViewManager<T, C> {
private static final String PROP_BACKGROUND_COLOR = ViewProps.BACKGROUND_COLOR;

View File

@ -0,0 +1,139 @@
// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react.uimanager;
import javax.annotation.Nullable;
import java.util.Locale;
import com.facebook.csslayout.CSSAlign;
import com.facebook.csslayout.CSSConstants;
import com.facebook.csslayout.CSSFlexDirection;
import com.facebook.csslayout.CSSJustify;
import com.facebook.csslayout.CSSPositionType;
import com.facebook.csslayout.CSSWrap;
/**
* Supply setters for base view layout properties such as width, height, flex properties,
* borders, etc.
*/
public class LayoutShadowNode extends ReactShadowNode {
@ReactProp(name = ViewProps.WIDTH, defaultFloat = CSSConstants.UNDEFINED)
public void setWidth(float width) {
setStyleWidth(CSSConstants.isUndefined(width) ? width : PixelUtil.toPixelFromDIP(width));
}
@ReactProp(name = ViewProps.HEIGHT, defaultFloat = CSSConstants.UNDEFINED)
public void setHeight(float height) {
setStyleHeight(CSSConstants.isUndefined(height) ? height : PixelUtil.toPixelFromDIP(height));
}
@ReactProp(name = ViewProps.LEFT, defaultFloat = CSSConstants.UNDEFINED)
public void setLeft(float left) {
setPositionLeft(CSSConstants.isUndefined(left) ? left : PixelUtil.toPixelFromDIP(left));
}
@ReactProp(name = ViewProps.TOP, defaultFloat = CSSConstants.UNDEFINED)
public void setTop(float top) {
setPositionTop(CSSConstants.isUndefined(top) ? top : PixelUtil.toPixelFromDIP(top));
}
@ReactProp(name = ViewProps.BOTTOM, defaultFloat = CSSConstants.UNDEFINED)
public void setBottom(float bottom) {
setPositionBottom(CSSConstants.isUndefined(bottom) ? bottom : PixelUtil.toPixelFromDIP(bottom));
}
@ReactProp(name = ViewProps.RIGHT, defaultFloat = CSSConstants.UNDEFINED)
public void setRight(float right) {
setPositionRight(CSSConstants.isUndefined(right) ? right : PixelUtil.toPixelFromDIP(right));
}
@ReactProp(name = ViewProps.FLEX, defaultFloat = 0f)
public void setFlex(float flex) {
super.setFlex(flex);
}
@ReactProp(name = ViewProps.FLEX_DIRECTION)
public void setFlexDirection(@Nullable String flexDirection) {
setFlexDirection(
flexDirection == null ? CSSFlexDirection.COLUMN : CSSFlexDirection.valueOf(
flexDirection.toUpperCase(Locale.US)));
}
@ReactProp(name = ViewProps.FLEX_WRAP)
public void setFlexWrap(@Nullable String flexWrap) {
setWrap(flexWrap == null ? CSSWrap.NOWRAP : CSSWrap.valueOf(flexWrap.toUpperCase(Locale.US)));
}
@ReactProp(name = ViewProps.ALIGN_SELF)
public void setAlignSelf(@Nullable String alignSelf) {
setAlignSelf(alignSelf == null ? CSSAlign.AUTO : CSSAlign.valueOf(
alignSelf.toUpperCase(Locale.US).replace("-", "_")));
}
@ReactProp(name = ViewProps.ALIGN_ITEMS)
public void setAlignItems(@Nullable String alignItems) {
setAlignItems(
alignItems == null ? CSSAlign.STRETCH : CSSAlign.valueOf(
alignItems.toUpperCase(Locale.US).replace("-", "_")));
}
@ReactProp(name = ViewProps.JUSTIFY_CONTENT)
public void setJustifyContent(@Nullable String justifyContent) {
setJustifyContent(justifyContent == null ? CSSJustify.FLEX_START : CSSJustify.valueOf(
justifyContent.toUpperCase(Locale.US).replace("-", "_")));
}
@ReactPropGroup(names = {
ViewProps.MARGIN,
ViewProps.MARGIN_VERTICAL,
ViewProps.MARGIN_HORIZONTAL,
ViewProps.MARGIN_LEFT,
ViewProps.MARGIN_RIGHT,
ViewProps.MARGIN_TOP,
ViewProps.MARGIN_BOTTOM,
}, defaultFloat = 0f)
public void setMargins(int index, float margin) {
setMargin(ViewProps.PADDING_MARGIN_SPACING_TYPES[index], PixelUtil.toPixelFromDIP(margin));
}
@ReactPropGroup(names = {
ViewProps.PADDING,
ViewProps.PADDING_VERTICAL,
ViewProps.PADDING_HORIZONTAL,
ViewProps.PADDING_LEFT,
ViewProps.PADDING_RIGHT,
ViewProps.PADDING_TOP,
ViewProps.PADDING_BOTTOM,
}, defaultFloat = CSSConstants.UNDEFINED)
public void setPaddings(int index, float padding) {
setPadding(
ViewProps.PADDING_MARGIN_SPACING_TYPES[index],
CSSConstants.isUndefined(padding) ? padding : PixelUtil.toPixelFromDIP(padding));
}
@ReactPropGroup(names = {
ViewProps.BORDER_WIDTH,
ViewProps.BORDER_LEFT_WIDTH,
ViewProps.BORDER_RIGHT_WIDTH,
ViewProps.BORDER_TOP_WIDTH,
ViewProps.BORDER_BOTTOM_WIDTH,
}, defaultFloat = 0f)
public void setBorderWidths(int index, float borderWidth) {
setBorder(ViewProps.BORDER_SPACING_TYPES[index], PixelUtil.toPixelFromDIP(borderWidth));
}
@ReactProp(name = ViewProps.POSITION)
public void setPosition(@Nullable String position) {
CSSPositionType positionType = position == null ?
CSSPositionType.RELATIVE : CSSPositionType.valueOf(position.toUpperCase(Locale.US));
setPositionType(positionType);
}
@Override
@ReactProp(name = "onLayout")
public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
super.setShouldNotifyOnLayout(shouldNotifyOnLayout);
}
}

View File

@ -167,20 +167,13 @@ public class ReactShadowNode extends CSSNode {
ViewManagersPropertyCache.getNativePropSettersForShadowNodeClass(getClass());
ReadableMap propMap = props.mBackingMap;
ReadableMapKeySeyIterator iterator = propMap.keySetIterator();
// TODO(krzysztof): Remove missingSetters code once all views are migrated to @ReactProp
boolean missingSetters = false;
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ViewManagersPropertyCache.PropSetter setter = propSetters.get(key);
if (setter != null) {
setter.updateShadowNodeProp(this, props);
} else {
missingSetters = true;
}
}
if (missingSetters) {
updateShadowNode(props);
}
onAfterUpdateTransaction();
}
@ -188,11 +181,6 @@ public class ReactShadowNode extends CSSNode {
// no-op
}
@Deprecated
public void updateShadowNode(CatalystStylesDiffMap styles) {
BaseCSSPropertyApplicator.applyCSSProperties(this, styles);
}
/**
* 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

View File

@ -20,16 +20,16 @@ import android.view.View;
* @param <T> the view handled by this manager
*/
public abstract class SimpleViewManager<T extends View> extends
BaseViewManager<T, ReactShadowNode> {
BaseViewManager<T, LayoutShadowNode> {
@Override
public ReactShadowNode createShadowNodeInstance() {
return new ReactShadowNode();
public LayoutShadowNode createShadowNodeInstance() {
return new LayoutShadowNode();
}
@Override
public Class<ReactShadowNode> getShadowNodeClass() {
return ReactShadowNode.class;
public Class<LayoutShadowNode> getShadowNodeClass() {
return LayoutShadowNode.class;
}
@Override

View File

@ -16,16 +16,16 @@ import android.view.ViewGroup;
* Class providing children management API for view managers of classes extending ViewGroup.
*/
public abstract class ViewGroupManager <T extends ViewGroup>
extends BaseViewManager<T, ReactShadowNode> {
extends BaseViewManager<T, LayoutShadowNode> {
@Override
public ReactShadowNode createShadowNodeInstance() {
return new ReactShadowNode();
public LayoutShadowNode createShadowNodeInstance() {
return new LayoutShadowNode();
}
@Override
public Class<ReactShadowNode> getShadowNodeClass() {
return ReactShadowNode.class;
public Class<LayoutShadowNode> getShadowNodeClass() {
return LayoutShadowNode.class;
}
@Override

View File

@ -34,14 +34,23 @@ public class ViewProps {
public static final String HEIGHT = "height";
public static final String JUSTIFY_CONTENT = "justifyContent";
public static final String LEFT = "left";
public static final String[] MARGINS = {
"margin", "marginVertical", "marginHorizontal", "marginLeft", "marginRight", "marginTop",
"marginBottom"
};
public static final String[] PADDINGS = {
"padding", "paddingVertical", "paddingHorizontal", "paddingLeft", "paddingRight",
"paddingTop", "paddingBottom"
};
public static final String MARGIN = "margin";
public static final String MARGIN_VERTICAL = "marginVertical";
public static final String MARGIN_HORIZONTAL = "marginHorizontal";
public static final String MARGIN_LEFT = "marginLeft";
public static final String MARGIN_RIGHT = "marginRight";
public static final String MARGIN_TOP = "marginTop";
public static final String MARGIN_BOTTOM = "marginBottom";
public static final String PADDING = "padding";
public static final String PADDING_VERTICAL = "paddingVertical";
public static final String PADDING_HORIZONTAL = "paddingHorizontal";
public static final String PADDING_LEFT = "paddingLeft";
public static final String PADDING_RIGHT = "paddingRight";
public static final String PADDING_TOP = "paddingTop";
public static final String PADDING_BOTTOM = "paddingBottom";
public static final String POSITION = "position";
public static final String RIGHT = "right";
public static final String TOP = "top";
@ -61,6 +70,7 @@ public class ViewProps {
public static final String ON = "on";
public static final String RESIZE_MODE = "resizeMode";
public static final String TEXT_ALIGN = "textAlign";
public static final String BORDER_WIDTH = "borderWidth";
public static final String BORDER_LEFT_WIDTH = "borderLeftWidth";
public static final String BORDER_TOP_WIDTH = "borderTopWidth";
@ -69,20 +79,13 @@ public class ViewProps {
public static final int[] BORDER_SPACING_TYPES = {
Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM
};
public static final String[] BORDER_WIDTHS = {
BORDER_WIDTH, BORDER_LEFT_WIDTH, BORDER_RIGHT_WIDTH, BORDER_TOP_WIDTH, BORDER_BOTTOM_WIDTH,
};
public static final int[] PADDING_MARGIN_SPACING_TYPES = {
Spacing.ALL, Spacing.VERTICAL, Spacing.HORIZONTAL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP,
Spacing.BOTTOM
};
private static final HashSet<String> LAYOUT_ONLY_PROPS = createLayoutOnlyPropsMap();
private static HashSet<String> createLayoutOnlyPropsMap() {
HashSet<String> layoutOnlyProps = SetBuilder.newHashSet();
layoutOnlyProps.addAll(
Arrays.asList(
private static final HashSet<String> LAYOUT_ONLY_PROPS = new HashSet<>(
Arrays.asList(
ALIGN_SELF,
ALIGN_ITEMS,
BOTTOM,
@ -96,15 +99,25 @@ public class ViewProps {
POSITION,
RIGHT,
TOP,
WIDTH));
for (int i = 0; i < MARGINS.length; i++) {
layoutOnlyProps.add(MARGINS[i]);
}
for (int i = 0; i < PADDINGS.length; i++) {
layoutOnlyProps.add(PADDINGS[i]);
}
return layoutOnlyProps;
}
WIDTH,
/* margins */
MARGIN,
MARGIN_VERTICAL,
MARGIN_HORIZONTAL,
MARGIN_LEFT,
MARGIN_RIGHT,
MARGIN_TOP,
MARGIN_BOTTOM,
/* paddings */
PADDING,
PADDING_VERTICAL,
PADDING_HORIZONTAL,
PADDING_LEFT,
PADDING_RIGHT,
PADDING_TOP,
PADDING_BOTTOM));
public static boolean isLayoutOnly(String prop) {
return LAYOUT_ONLY_PROPS.contains(prop);

View File

@ -21,15 +21,15 @@ import android.widget.ProgressBar;
import com.facebook.csslayout.CSSNode;
import com.facebook.csslayout.MeasureOutput;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
/**
* Node responsible for holding the style of the ProgressBar, see under
* {@link android.R.attr.progressBarStyle} for possible styles. ReactProgressBarViewManager
* manages how this style is applied to the ProgressBar.
*/
public class ProgressBarShadowNode extends ReactShadowNode implements CSSNode.MeasureFunction {
public class ProgressBarShadowNode extends LayoutShadowNode implements CSSNode.MeasureFunction {
private String mStyle = ReactProgressBarViewManager.DEFAULT_STYLE;

View File

@ -18,11 +18,12 @@ import android.widget.CompoundButton;
import com.facebook.csslayout.CSSNode;
import com.facebook.csslayout.MeasureOutput;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewProps;
/**
@ -32,7 +33,7 @@ public class ReactSwitchManager extends SimpleViewManager<ReactSwitch> {
private static final String REACT_CLASS = "AndroidSwitch";
private static class ReactSwitchShadowNode extends ReactShadowNode implements
private static class ReactSwitchShadowNode extends LayoutShadowNode implements
CSSNode.MeasureFunction {
private int mWidth;
@ -82,7 +83,7 @@ public class ReactSwitchManager extends SimpleViewManager<ReactSwitch> {
}
@Override
public ReactShadowNode createShadowNodeInstance() {
public LayoutShadowNode createShadowNodeInstance() {
return new ReactSwitchShadowNode();
}

View File

@ -33,6 +33,7 @@ import com.facebook.csslayout.MeasureOutput;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.ReactProp;
import com.facebook.react.uimanager.ReactShadowNode;
@ -53,7 +54,7 @@ import com.facebook.react.uimanager.ViewProps;
* TODO(7255858): Rename *CSSNode to *ShadowView (or sth similar) as it's no longer is used
* solely for layouting
*/
public class ReactTextShadowNode extends ReactShadowNode {
public class ReactTextShadowNode extends LayoutShadowNode {
public static final int UNSET = -1;