diff --git a/React/CSSLayout/CSSLayout.c b/React/CSSLayout/CSSLayout.c index 9fa990b41..a43019c4a 100644 --- a/React/CSSLayout/CSSLayout.c +++ b/React/CSSLayout/CSSLayout.c @@ -85,6 +85,9 @@ typedef struct CSSStyle { float dimensions[2]; float minDimensions[2]; float maxDimensions[2]; + + // Yoga specific properties, not compatible with flexbox specification + float aspectRatio; } CSSStyle; typedef struct CSSNode { @@ -269,6 +272,8 @@ void CSSNodeInit(const CSSNodeRef node) { node->style.border[edge] = CSSUndefined; } + node->style.aspectRatio = CSSUndefined; + node->layout.dimensions[CSSDimensionWidth] = CSSUndefined; node->layout.dimensions[CSSDimensionHeight] = CSSUndefined; @@ -459,6 +464,9 @@ CSS_NODE_STYLE_PROPERTY_IMPL(float, MinHeight, minHeight, minDimensions[CSSDimen CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxWidth, maxWidth, maxDimensions[CSSDimensionWidth]); CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxHeight, maxHeight, maxDimensions[CSSDimensionHeight]); +// Yoga specific properties, not compatible with flexbox specification +CSS_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); + CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[CSSEdgeLeft]); CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[CSSEdgeTop]); CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[CSSEdgeRight]); @@ -1032,6 +1040,20 @@ static void computeChildFlexBasis(const CSSNodeRef node, childHeightMeasureMode = CSSMeasureModeExactly; } + if (!CSSValueIsUndefined(child->style.aspectRatio)) { + if (!isMainAxisRow && childWidthMeasureMode == CSSMeasureModeExactly) { + child->layout.computedFlexBasis = + fmaxf(childWidth * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionColumn)); + return; + } else if (isMainAxisRow && childHeightMeasureMode == CSSMeasureModeExactly) { + child->layout.computedFlexBasis = + fmaxf(childHeight * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionRow)); + return; + } + } + constrainMaxSizeForMode(child->style.maxDimensions[CSSDimensionWidth], &childWidthMeasureMode, &childWidth); @@ -1108,6 +1130,20 @@ static void absoluteLayoutChild(const CSSNodeRef node, } } + // Exactly one dimension needs to be defined for us to be able to do aspect ratio + // calculation. One dimension being the anchor and the other being flexible. + if (CSSValueIsUndefined(childWidth) ^ CSSValueIsUndefined(childHeight)) { + if (!CSSValueIsUndefined(child->style.aspectRatio)) { + if (CSSValueIsUndefined(childWidth)) { + childWidth = fmaxf(childHeight * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionColumn)); + } else if (CSSValueIsUndefined(childHeight)) { + childHeight = fmaxf(childWidth * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionRow)); + } + } + } + // If we're still missing one or the other dimension, measure the content. if (CSSValueIsUndefined(childWidth) || CSSValueIsUndefined(childHeight)) { childWidthMeasureMode = @@ -1774,6 +1810,19 @@ static void layoutNodeImpl(const CSSNodeRef node, } } + if (!CSSValueIsUndefined(currentRelativeChild->style.aspectRatio)) { + if (isMainAxisRow && childHeightMeasureMode != CSSMeasureModeExactly) { + childHeight = + fmaxf(childWidth * currentRelativeChild->style.aspectRatio, + getPaddingAndBorderAxis(currentRelativeChild, CSSFlexDirectionColumn)); + childHeightMeasureMode = CSSMeasureModeExactly; + } else if (!isMainAxisRow && childWidthMeasureMode != CSSMeasureModeExactly) { + childWidth = fmaxf(childHeight * currentRelativeChild->style.aspectRatio, + getPaddingAndBorderAxis(currentRelativeChild, CSSFlexDirectionRow)); + childWidthMeasureMode = CSSMeasureModeExactly; + } + } + constrainMaxSizeForMode(currentRelativeChild->style.maxDimensions[CSSDimensionWidth], &childWidthMeasureMode, &childWidth); diff --git a/React/CSSLayout/CSSLayout.h b/React/CSSLayout/CSSLayout.h index e052b07e8..7bcc244e9 100644 --- a/React/CSSLayout/CSSLayout.h +++ b/React/CSSLayout/CSSLayout.h @@ -149,6 +149,15 @@ CSS_NODE_STYLE_PROPERTY(float, MinHeight, minHeight); CSS_NODE_STYLE_PROPERTY(float, MaxWidth, maxWidth); CSS_NODE_STYLE_PROPERTY(float, MaxHeight, maxHeight); +// Yoga specific properties, not compatible with flexbox specification +// Aspect ratio control the size of the undefined dimension of a node. +// - On a node with a set width/height aspect ratio control the size of the unset dimension +// - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if unset +// - On a node with a measure function aspect ratio works as though the measure function measures the flex basis +// - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if unset +// - Aspect ratio takes min/max dimensions into account +CSS_NODE_STYLE_PROPERTY(float, AspectRatio, aspectRatio); + CSS_NODE_LAYOUT_PROPERTY(float, Left); CSS_NODE_LAYOUT_PROPERTY(float, Top); CSS_NODE_LAYOUT_PROPERTY(float, Right); diff --git a/ReactAndroid/src/main/java/com/facebook/csslayout/CSSNode.java b/ReactAndroid/src/main/java/com/facebook/csslayout/CSSNode.java index 95a5319e5..d1943e9cc 100644 --- a/ReactAndroid/src/main/java/com/facebook/csslayout/CSSNode.java +++ b/ReactAndroid/src/main/java/com/facebook/csslayout/CSSNode.java @@ -476,6 +476,16 @@ public class CSSNode implements CSSNodeAPI { jni_CSSNodeStyleSetMaxHeight(mNativePointer, maxheight); } + private native float jni_CSSNodeStyleGetAspectRatio(long nativePointer); + public float getStyleAspectRatio() { + return jni_CSSNodeStyleGetAspectRatio(mNativePointer); + } + + private native void jni_CSSNodeStyleSetAspectRatio(long nativePointer, float aspectRatio); + public void setStyleAspectRatio(float aspectRatio) { + jni_CSSNodeStyleSetAspectRatio(mNativePointer, aspectRatio); + } + @Override public float getLayoutX() { return mLeft; diff --git a/ReactAndroid/src/main/jni/first-party/csslayoutjni/jni/CSSJNI.cpp b/ReactAndroid/src/main/jni/first-party/csslayoutjni/jni/CSSJNI.cpp index 2c6ef3af9..b55433906 100644 --- a/ReactAndroid/src/main/jni/first-party/csslayoutjni/jni/CSSJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/csslayoutjni/jni/CSSJNI.cpp @@ -249,6 +249,9 @@ CSS_NODE_JNI_STYLE_PROP(jfloat, float, Height); CSS_NODE_JNI_STYLE_PROP(jfloat, float, MinHeight); CSS_NODE_JNI_STYLE_PROP(jfloat, float, MaxHeight); +// Yoga specific properties, not compatible with flexbox specification +CSS_NODE_JNI_STYLE_PROP(jfloat, float, AspectRatio); + #define CSSMakeNativeMethod(name) makeNativeMethod(#name, name) jint JNI_OnLoad(JavaVM *vm, void *) { @@ -312,6 +315,8 @@ jint JNI_OnLoad(JavaVM *vm, void *) { CSSMakeNativeMethod(jni_CSSNodeStyleSetMaxWidth), CSSMakeNativeMethod(jni_CSSNodeStyleGetMaxHeight), CSSMakeNativeMethod(jni_CSSNodeStyleSetMaxHeight), + CSSMakeNativeMethod(jni_CSSNodeStyleGetAspectRatio), + CSSMakeNativeMethod(jni_CSSNodeStyleSetAspectRatio), CSSMakeNativeMethod(jni_CSSNodeGetInstanceCount), CSSMakeNativeMethod(jni_CSSLayoutSetLogger), diff --git a/ReactCommon/CSSLayout/CSSLayout/CSSLayout.c b/ReactCommon/CSSLayout/CSSLayout/CSSLayout.c index 9fa990b41..a43019c4a 100644 --- a/ReactCommon/CSSLayout/CSSLayout/CSSLayout.c +++ b/ReactCommon/CSSLayout/CSSLayout/CSSLayout.c @@ -85,6 +85,9 @@ typedef struct CSSStyle { float dimensions[2]; float minDimensions[2]; float maxDimensions[2]; + + // Yoga specific properties, not compatible with flexbox specification + float aspectRatio; } CSSStyle; typedef struct CSSNode { @@ -269,6 +272,8 @@ void CSSNodeInit(const CSSNodeRef node) { node->style.border[edge] = CSSUndefined; } + node->style.aspectRatio = CSSUndefined; + node->layout.dimensions[CSSDimensionWidth] = CSSUndefined; node->layout.dimensions[CSSDimensionHeight] = CSSUndefined; @@ -459,6 +464,9 @@ CSS_NODE_STYLE_PROPERTY_IMPL(float, MinHeight, minHeight, minDimensions[CSSDimen CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxWidth, maxWidth, maxDimensions[CSSDimensionWidth]); CSS_NODE_STYLE_PROPERTY_IMPL(float, MaxHeight, maxHeight, maxDimensions[CSSDimensionHeight]); +// Yoga specific properties, not compatible with flexbox specification +CSS_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); + CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[CSSEdgeLeft]); CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[CSSEdgeTop]); CSS_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[CSSEdgeRight]); @@ -1032,6 +1040,20 @@ static void computeChildFlexBasis(const CSSNodeRef node, childHeightMeasureMode = CSSMeasureModeExactly; } + if (!CSSValueIsUndefined(child->style.aspectRatio)) { + if (!isMainAxisRow && childWidthMeasureMode == CSSMeasureModeExactly) { + child->layout.computedFlexBasis = + fmaxf(childWidth * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionColumn)); + return; + } else if (isMainAxisRow && childHeightMeasureMode == CSSMeasureModeExactly) { + child->layout.computedFlexBasis = + fmaxf(childHeight * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionRow)); + return; + } + } + constrainMaxSizeForMode(child->style.maxDimensions[CSSDimensionWidth], &childWidthMeasureMode, &childWidth); @@ -1108,6 +1130,20 @@ static void absoluteLayoutChild(const CSSNodeRef node, } } + // Exactly one dimension needs to be defined for us to be able to do aspect ratio + // calculation. One dimension being the anchor and the other being flexible. + if (CSSValueIsUndefined(childWidth) ^ CSSValueIsUndefined(childHeight)) { + if (!CSSValueIsUndefined(child->style.aspectRatio)) { + if (CSSValueIsUndefined(childWidth)) { + childWidth = fmaxf(childHeight * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionColumn)); + } else if (CSSValueIsUndefined(childHeight)) { + childHeight = fmaxf(childWidth * child->style.aspectRatio, + getPaddingAndBorderAxis(child, CSSFlexDirectionRow)); + } + } + } + // If we're still missing one or the other dimension, measure the content. if (CSSValueIsUndefined(childWidth) || CSSValueIsUndefined(childHeight)) { childWidthMeasureMode = @@ -1774,6 +1810,19 @@ static void layoutNodeImpl(const CSSNodeRef node, } } + if (!CSSValueIsUndefined(currentRelativeChild->style.aspectRatio)) { + if (isMainAxisRow && childHeightMeasureMode != CSSMeasureModeExactly) { + childHeight = + fmaxf(childWidth * currentRelativeChild->style.aspectRatio, + getPaddingAndBorderAxis(currentRelativeChild, CSSFlexDirectionColumn)); + childHeightMeasureMode = CSSMeasureModeExactly; + } else if (!isMainAxisRow && childWidthMeasureMode != CSSMeasureModeExactly) { + childWidth = fmaxf(childHeight * currentRelativeChild->style.aspectRatio, + getPaddingAndBorderAxis(currentRelativeChild, CSSFlexDirectionRow)); + childWidthMeasureMode = CSSMeasureModeExactly; + } + } + constrainMaxSizeForMode(currentRelativeChild->style.maxDimensions[CSSDimensionWidth], &childWidthMeasureMode, &childWidth); diff --git a/ReactCommon/CSSLayout/CSSLayout/CSSLayout.h b/ReactCommon/CSSLayout/CSSLayout/CSSLayout.h index e052b07e8..7bcc244e9 100644 --- a/ReactCommon/CSSLayout/CSSLayout/CSSLayout.h +++ b/ReactCommon/CSSLayout/CSSLayout/CSSLayout.h @@ -149,6 +149,15 @@ CSS_NODE_STYLE_PROPERTY(float, MinHeight, minHeight); CSS_NODE_STYLE_PROPERTY(float, MaxWidth, maxWidth); CSS_NODE_STYLE_PROPERTY(float, MaxHeight, maxHeight); +// Yoga specific properties, not compatible with flexbox specification +// Aspect ratio control the size of the undefined dimension of a node. +// - On a node with a set width/height aspect ratio control the size of the unset dimension +// - On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if unset +// - On a node with a measure function aspect ratio works as though the measure function measures the flex basis +// - On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if unset +// - Aspect ratio takes min/max dimensions into account +CSS_NODE_STYLE_PROPERTY(float, AspectRatio, aspectRatio); + CSS_NODE_LAYOUT_PROPERTY(float, Left); CSS_NODE_LAYOUT_PROPERTY(float, Top); CSS_NODE_LAYOUT_PROPERTY(float, Right);