mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-03 17:45:12 +08:00
Summary: This diff adds margin:auto (https://drafts.csswg.org/css-flexbox-1/#auto-margins) support to React Native. This enables layout not previously supported without inserting empty 'spacer' views. See below Playground for usage. ``` class Playground extends React.Component { render() { return ( <View style={{width: '100%', height: '100%', flexDirection: 'row', backgroundColor: 'white'}}> <View style={{width: 100, height: 100, backgroundColor: 'red'}}/> <View style={{width: 100, height: 100, marginLeft: 'auto', backgroundColor: 'blue'}}/> </View> ); } } ``` Reviewed By: astreet Differential Revision: D4611753 fbshipit-source-id: e78335565c193f7fb263129a638b444715ba5ab0
418 lines
11 KiB
Java
418 lines
11 KiB
Java
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
package com.facebook.react.uimanager;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import java.util.Locale;
|
|
|
|
import com.facebook.react.bridge.Dynamic;
|
|
import com.facebook.react.bridge.ReadableType;
|
|
|
|
import com.facebook.yoga.YogaAlign;
|
|
import com.facebook.yoga.YogaConstants;
|
|
import com.facebook.yoga.YogaFlexDirection;
|
|
import com.facebook.yoga.YogaJustify;
|
|
import com.facebook.yoga.YogaOverflow;
|
|
import com.facebook.yoga.YogaPositionType;
|
|
import com.facebook.yoga.YogaUnit;
|
|
import com.facebook.yoga.YogaWrap;
|
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
|
|
|
/**
|
|
* Supply setters for base view layout properties such as width, height, flex properties,
|
|
* borders, etc.
|
|
*
|
|
* Checking for isVirtual everywhere is a hack to get around the fact that some virtual nodes still
|
|
* have layout properties set on them in JS: for example, a component that returns a <Text> 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 {
|
|
|
|
/**
|
|
* A Mutable version of com.facebook.yoga.YogaValue
|
|
*/
|
|
private static class MutableYogaValue {
|
|
float value;
|
|
YogaUnit unit;
|
|
|
|
void setFromDynamic(Dynamic dynamic) {
|
|
if (dynamic.isNull()) {
|
|
unit = YogaUnit.UNDEFINED;
|
|
value = YogaConstants.UNDEFINED;
|
|
} else if (dynamic.getType() == ReadableType.String) {
|
|
final String s = dynamic.asString();
|
|
if (s.equals("auto")) {
|
|
unit = YogaUnit.AUTO;
|
|
value = YogaConstants.UNDEFINED;
|
|
} else if (s.endsWith("%")) {
|
|
unit = YogaUnit.PERCENT;
|
|
value = Float.parseFloat(s.substring(0, s.length() - 1));
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown value: " + s);
|
|
}
|
|
} else {
|
|
unit = YogaUnit.POINT;
|
|
value = PixelUtil.toPixelFromDIP(dynamic.asDouble());
|
|
}
|
|
}
|
|
}
|
|
|
|
private final MutableYogaValue mTempYogaValue = new MutableYogaValue();
|
|
|
|
@ReactProp(name = ViewProps.WIDTH)
|
|
public void setWidth(Dynamic width) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(width);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleWidth(mTempYogaValue.value);
|
|
break;
|
|
case AUTO:
|
|
setStyleWidthAuto();
|
|
break;
|
|
case PERCENT:
|
|
setStyleWidthPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
width.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.MIN_WIDTH)
|
|
public void setMinWidth(Dynamic minWidth) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(minWidth);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleMinWidth(mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setStyleMinWidthPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
minWidth.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.MAX_WIDTH)
|
|
public void setMaxWidth(Dynamic maxWidth) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(maxWidth);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleMaxWidth(mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setStyleMaxWidthPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
maxWidth.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.HEIGHT)
|
|
public void setHeight(Dynamic height) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(height);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleHeight(mTempYogaValue.value);
|
|
break;
|
|
case AUTO:
|
|
setStyleHeightAuto();
|
|
break;
|
|
case PERCENT:
|
|
setStyleHeightPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
height.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.MIN_HEIGHT)
|
|
public void setMinHeight(Dynamic minHeight) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(minHeight);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleMinHeight(mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setStyleMinHeightPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
minHeight.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.MAX_HEIGHT)
|
|
public void setMaxHeight(Dynamic maxHeight) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(maxHeight);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setStyleMaxHeight(mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setStyleMaxHeightPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
maxHeight.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX, defaultFloat = 0f)
|
|
public void setFlex(float flex) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
super.setFlex(flex);
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX_GROW, defaultFloat = 0f)
|
|
public void setFlexGrow(float flexGrow) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
super.setFlexGrow(flexGrow);
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX_SHRINK, defaultFloat = 0f)
|
|
public void setFlexShrink(float flexShrink) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
super.setFlexShrink(flexShrink);
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX_BASIS)
|
|
public void setFlexBasis(Dynamic flexBasis) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(flexBasis);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setFlexBasis(mTempYogaValue.value);
|
|
break;
|
|
case AUTO:
|
|
setFlexBasisAuto();
|
|
break;
|
|
case PERCENT:
|
|
setFlexBasisPercent(mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
flexBasis.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.ASPECT_RATIO, defaultFloat = YogaConstants.UNDEFINED)
|
|
public void setAspectRatio(float aspectRatio) {
|
|
setStyleAspectRatio(aspectRatio);
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX_DIRECTION)
|
|
public void setFlexDirection(@Nullable String flexDirection) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setFlexDirection(
|
|
flexDirection == null ? YogaFlexDirection.COLUMN : YogaFlexDirection.valueOf(
|
|
flexDirection.toUpperCase(Locale.US).replace("-", "_")));
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.FLEX_WRAP)
|
|
public void setFlexWrap(@Nullable String flexWrap) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
if (flexWrap == null || flexWrap.equals("nowrap")) {
|
|
setFlexWrap(YogaWrap.NO_WRAP);
|
|
} else if (flexWrap.equals("wrap")) {
|
|
setFlexWrap(YogaWrap.WRAP);
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown flexWrap value: " + flexWrap);
|
|
}
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.ALIGN_SELF)
|
|
public void setAlignSelf(@Nullable String alignSelf) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setAlignSelf(alignSelf == null ? YogaAlign.AUTO : YogaAlign.valueOf(
|
|
alignSelf.toUpperCase(Locale.US).replace("-", "_")));
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.ALIGN_ITEMS)
|
|
public void setAlignItems(@Nullable String alignItems) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setAlignItems(
|
|
alignItems == null ? YogaAlign.STRETCH : YogaAlign.valueOf(
|
|
alignItems.toUpperCase(Locale.US).replace("-", "_")));
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.JUSTIFY_CONTENT)
|
|
public void setJustifyContent(@Nullable String justifyContent) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setJustifyContent(justifyContent == null ? YogaJustify.FLEX_START : YogaJustify.valueOf(
|
|
justifyContent.toUpperCase(Locale.US).replace("-", "_")));
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.OVERFLOW)
|
|
public void setOverflow(@Nullable String overflow) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setOverflow(overflow == null ? YogaOverflow.VISIBLE : YogaOverflow.valueOf(
|
|
overflow.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,
|
|
})
|
|
public void setMargins(int index, Dynamic margin) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(margin);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setMargin(ViewProps.PADDING_MARGIN_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
case AUTO:
|
|
setMarginAuto(ViewProps.PADDING_MARGIN_SPACING_TYPES[index]);
|
|
break;
|
|
case PERCENT:
|
|
setMarginPercent(ViewProps.PADDING_MARGIN_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
margin.recycle();
|
|
}
|
|
|
|
@ReactPropGroup(names = {
|
|
ViewProps.PADDING,
|
|
ViewProps.PADDING_VERTICAL,
|
|
ViewProps.PADDING_HORIZONTAL,
|
|
ViewProps.PADDING_LEFT,
|
|
ViewProps.PADDING_RIGHT,
|
|
ViewProps.PADDING_TOP,
|
|
ViewProps.PADDING_BOTTOM,
|
|
})
|
|
public void setPaddings(int index, Dynamic padding) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(padding);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setPadding(ViewProps.PADDING_MARGIN_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setPaddingPercent(ViewProps.PADDING_MARGIN_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
padding.recycle();
|
|
}
|
|
|
|
@ReactPropGroup(names = {
|
|
ViewProps.BORDER_WIDTH,
|
|
ViewProps.BORDER_LEFT_WIDTH,
|
|
ViewProps.BORDER_RIGHT_WIDTH,
|
|
ViewProps.BORDER_TOP_WIDTH,
|
|
ViewProps.BORDER_BOTTOM_WIDTH,
|
|
}, defaultFloat = YogaConstants.UNDEFINED)
|
|
public void setBorderWidths(int index, float borderWidth) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
setBorder(ViewProps.BORDER_SPACING_TYPES[index], PixelUtil.toPixelFromDIP(borderWidth));
|
|
}
|
|
|
|
@ReactPropGroup(names = {
|
|
ViewProps.LEFT,
|
|
ViewProps.RIGHT,
|
|
ViewProps.TOP,
|
|
ViewProps.BOTTOM,
|
|
})
|
|
public void setPositionValues(int index, Dynamic position) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
mTempYogaValue.setFromDynamic(position);
|
|
switch (mTempYogaValue.unit) {
|
|
case POINT:
|
|
case UNDEFINED:
|
|
setPosition(ViewProps.POSITION_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
case PERCENT:
|
|
setPositionPercent(ViewProps.POSITION_SPACING_TYPES[index], mTempYogaValue.value);
|
|
break;
|
|
}
|
|
|
|
position.recycle();
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.POSITION)
|
|
public void setPosition(@Nullable String position) {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
YogaPositionType positionType = position == null ?
|
|
YogaPositionType.RELATIVE : YogaPositionType.valueOf(position.toUpperCase(Locale.US));
|
|
setPositionType(positionType);
|
|
}
|
|
|
|
@Override
|
|
@ReactProp(name = "onLayout")
|
|
public void setShouldNotifyOnLayout(boolean shouldNotifyOnLayout) {
|
|
super.setShouldNotifyOnLayout(shouldNotifyOnLayout);
|
|
}
|
|
}
|