From cc275557be3cb8ecb1d535b4e136fd75cd3e9227 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Wed, 1 Mar 2017 09:12:45 -0800 Subject: [PATCH] Add margin auto support to react native 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 ( ); } } ``` Reviewed By: astreet Differential Revision: D4611753 fbshipit-source-id: e78335565c193f7fb263129a638b444715ba5ab0 --- React/Base/RCTConvert.m | 4 +- React/Views/RCTShadowView.m | 66 ++++++++++++++----- .../react/uimanager/LayoutShadowNode.java | 12 ++++ .../react/uimanager/ReactShadowNode.java | 16 +++++ 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/React/Base/RCTConvert.m b/React/Base/RCTConvert.m index d8a0e9df2..709b1278b 100644 --- a/React/Base/RCTConvert.m +++ b/React/Base/RCTConvert.m @@ -507,7 +507,9 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[ return (YGValue) { [json floatValue], YGUnitPoint }; } else if ([json isKindOfClass:[NSString class]]) { NSString *s = (NSString *) json; - if ([s hasSuffix:@"%"]) { + if ([s isEqualToString:@"auto"]) { + return (YGValue) { YGUndefined, YGUnitAuto }; + } else if ([s hasSuffix:@"%"]) { return (YGValue) { [[s substringToIndex:s.length] floatValue], YGUnitPercent }; } else { RCTLogConvertError(json, @"a YGValue. Did you forget the % or pt suffix?"); diff --git a/React/Views/RCTShadowView.m b/React/Views/RCTShadowView.m index 8a82794ed..e6ca99b8b 100644 --- a/React/Views/RCTShadowView.m +++ b/React/Views/RCTShadowView.m @@ -70,19 +70,41 @@ switch (ygvalue.unit) { \ break; \ } -#define DEFINE_PROCESS_META_PROPS(type) \ -static void RCTProcessMetaProps##type(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) { \ - RCT_SET_YGVALUE(metaProps[META_PROP_LEFT], YGNodeStyleSet##type, node, YGEdgeStart); \ - RCT_SET_YGVALUE(metaProps[META_PROP_RIGHT], YGNodeStyleSet##type, node, YGEdgeEnd); \ - RCT_SET_YGVALUE(metaProps[META_PROP_TOP], YGNodeStyleSet##type, node, YGEdgeTop); \ - RCT_SET_YGVALUE(metaProps[META_PROP_BOTTOM], YGNodeStyleSet##type, node, YGEdgeBottom); \ - RCT_SET_YGVALUE(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSet##type, node, YGEdgeHorizontal); \ - RCT_SET_YGVALUE(metaProps[META_PROP_VERTICAL], YGNodeStyleSet##type, node, YGEdgeVertical); \ - RCT_SET_YGVALUE(metaProps[META_PROP_ALL], YGNodeStyleSet##type, node, YGEdgeAll); \ +#define RCT_SET_YGVALUE_AUTO(ygvalue, setter, ...) \ +switch (ygvalue.unit) { \ + case YGUnitAuto: \ + setter##Auto(__VA_ARGS__); \ + break; \ + case YGUnitUndefined: \ + setter(__VA_ARGS__, YGUndefined); \ + break; \ + case YGUnitPoint: \ + setter(__VA_ARGS__, ygvalue.value); \ + break; \ + case YGUnitPercent: \ + setter##Percent(__VA_ARGS__, ygvalue.value); \ + break; \ } -DEFINE_PROCESS_META_PROPS(Padding); -DEFINE_PROCESS_META_PROPS(Margin); +static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) { + RCT_SET_YGVALUE(metaProps[META_PROP_LEFT], YGNodeStyleSetPadding, node, YGEdgeStart); + RCT_SET_YGVALUE(metaProps[META_PROP_RIGHT], YGNodeStyleSetPadding, node, YGEdgeEnd); + RCT_SET_YGVALUE(metaProps[META_PROP_TOP], YGNodeStyleSetPadding, node, YGEdgeTop); + RCT_SET_YGVALUE(metaProps[META_PROP_BOTTOM], YGNodeStyleSetPadding, node, YGEdgeBottom); + RCT_SET_YGVALUE(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetPadding, node, YGEdgeHorizontal); + RCT_SET_YGVALUE(metaProps[META_PROP_VERTICAL], YGNodeStyleSetPadding, node, YGEdgeVertical); + RCT_SET_YGVALUE(metaProps[META_PROP_ALL], YGNodeStyleSetPadding, node, YGEdgeAll); +} + +static void RCTProcessMetaPropsMargin(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) { + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_LEFT], YGNodeStyleSetMargin, node, YGEdgeStart); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_RIGHT], YGNodeStyleSetMargin, node, YGEdgeEnd); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_TOP], YGNodeStyleSetMargin, node, YGEdgeTop); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_BOTTOM], YGNodeStyleSetMargin, node, YGEdgeBottom); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetMargin, node, YGEdgeHorizontal); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_VERTICAL], YGNodeStyleSetMargin, node, YGEdgeVertical); + RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_ALL], YGNodeStyleSetMargin, node, YGEdgeAll); +} static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node) { YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_LEFT].value); @@ -522,9 +544,19 @@ RCT_BORDER_PROPERTY(Bottom, BOTTOM) RCT_BORDER_PROPERTY(Right, RIGHT) // Dimensions - #define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \ - (void)set##setProp:(YGValue)value \ +{ \ + RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \ + [self dirtyText]; \ +} \ +- (YGValue)getProp \ +{ \ + return YGNodeStyleGet##cssProp(_yogaNode); \ +} + +#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \ +- (void)set##setProp:(YGValue)value \ { \ RCT_SET_YGVALUE(value, YGNodeStyleSet##cssProp, _yogaNode); \ [self dirtyText]; \ @@ -536,10 +568,10 @@ RCT_BORDER_PROPERTY(Right, RIGHT) RCT_DIMENSION_PROPERTY(Width, width, Width) RCT_DIMENSION_PROPERTY(Height, height, Height) -RCT_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth) -RCT_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight) -RCT_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth) -RCT_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight) +RCT_MIN_MAX_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth) +RCT_MIN_MAX_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight) +RCT_MIN_MAX_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth) +RCT_MIN_MAX_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight) // Position @@ -644,7 +676,7 @@ static inline YGSize RCTShadowViewMeasure(YGNodeRef node, float width, YGMeasure - (void)setFlexBasis:(YGValue)value { - RCT_SET_YGVALUE(value, YGNodeStyleSetFlexBasis, _yogaNode); + RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSetFlexBasis, _yogaNode); } - (YGValue)flexBasis diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java index f80f85d44..17b6217e8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java @@ -74,6 +74,9 @@ public class LayoutShadowNode extends ReactShadowNode { case UNDEFINED: setStyleWidth(mTempYogaValue.value); break; + case AUTO: + setStyleWidthAuto(); + break; case PERCENT: setStyleWidthPercent(mTempYogaValue.value); break; @@ -134,6 +137,9 @@ public class LayoutShadowNode extends ReactShadowNode { case UNDEFINED: setStyleHeight(mTempYogaValue.value); break; + case AUTO: + setStyleHeightAuto(); + break; case PERCENT: setStyleHeightPercent(mTempYogaValue.value); break; @@ -218,6 +224,9 @@ public class LayoutShadowNode extends ReactShadowNode { case UNDEFINED: setFlexBasis(mTempYogaValue.value); break; + case AUTO: + setFlexBasisAuto(); + break; case PERCENT: setFlexBasisPercent(mTempYogaValue.value); break; @@ -312,6 +321,9 @@ public class LayoutShadowNode extends ReactShadowNode { 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; 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 6bcfa50a1..8fda24b9e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java @@ -554,6 +554,10 @@ public class ReactShadowNode { mYogaNode.setWidthPercent(percent); } + public void setStyleWidthAuto() { + mYogaNode.setWidthAuto(); + } + public void setStyleMinWidth(float widthPx) { mYogaNode.setMinWidth(widthPx); } @@ -582,6 +586,10 @@ public class ReactShadowNode { mYogaNode.setHeightPercent(percent); } + public void setStyleHeightAuto() { + mYogaNode.setHeightAuto(); + } + public void setStyleMinHeight(float widthPx) { mYogaNode.setMinHeight(widthPx); } @@ -614,6 +622,10 @@ public class ReactShadowNode { mYogaNode.setFlexBasis(flexBasis); } + public void setFlexBasisAuto() { + mYogaNode.setFlexBasisAuto(); + } + public void setFlexBasisPercent(float percent) { mYogaNode.setFlexBasisPercent(percent); } @@ -654,6 +666,10 @@ public class ReactShadowNode { 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)); }