From 55fc795686a7b5c4297cf7245de709f6c6d41741 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Mon, 21 Nov 2016 10:12:26 -0800 Subject: [PATCH] Add aspectRatio style property Summary: Implement aspect ratio as part of Yoga. Aspect ratio allows users of the library to specify the size of the undefined dimension in terms of an aspect ratio. See test cases for examples. Reviewed By: gkassabli Differential Revision: D4211458 fbshipit-source-id: f8d0d318369c7b529ee29e61a52b17d0cf3b396d --- CSSLayout/CSSLayout.c | 49 +++ CSSLayout/CSSLayout.h | 9 + CSSLayoutKit/UIView+CSSLayout.h | 3 + CSSLayoutKit/UIView+CSSLayout.m | 5 + csharp/Facebook.CSSLayout/CSSNode.cs | 13 + csharp/Facebook.CSSLayout/Native.cs | 6 + java/com/facebook/csslayout/CSSNode.java | 10 + java/jni/CSSJNI.cpp | 5 + tests/CSSLayoutAspectRatioTest.cpp | 407 +++++++++++++++++++++++ 9 files changed, 507 insertions(+) create mode 100644 tests/CSSLayoutAspectRatioTest.cpp diff --git a/CSSLayout/CSSLayout.c b/CSSLayout/CSSLayout.c index 9fa990b4..a43019c4 100644 --- a/CSSLayout/CSSLayout.c +++ b/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/CSSLayout/CSSLayout.h b/CSSLayout/CSSLayout.h index e052b07e..7bcc244e 100644 --- a/CSSLayout/CSSLayout.h +++ b/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/CSSLayoutKit/UIView+CSSLayout.h b/CSSLayoutKit/UIView+CSSLayout.h index d7702d09..d8d5ef77 100644 --- a/CSSLayoutKit/UIView+CSSLayout.h +++ b/CSSLayoutKit/UIView+CSSLayout.h @@ -46,6 +46,9 @@ - (void)css_setMaxWidth:(CGFloat)maxWidth; - (void)css_setMaxHeight:(CGFloat)maxHeight; +// Yoga specific properties, not compatible with flexbox specification +- (void)css_setAspectRatio:(CGFloat)aspectRatio; + /** Get the resolved direction of this node. This won't be CSSDirectionInherit */ diff --git a/CSSLayoutKit/UIView+CSSLayout.m b/CSSLayoutKit/UIView+CSSLayout.m index f166b5ff..db5e1b98 100644 --- a/CSSLayoutKit/UIView+CSSLayout.m +++ b/CSSLayoutKit/UIView+CSSLayout.m @@ -165,6 +165,11 @@ CSSNodeStyleSetMaxHeight([self cssNode], maxHeight); } +- (void)css_setAspectRatio:(CGFloat)aspectRatio +{ + CSSNodeStyleSetAspectRatio([self cssNode], aspectRatio); +} + #pragma mark - Layout and Sizing - (CSSDirection)css_resolvedDirection diff --git a/csharp/Facebook.CSSLayout/CSSNode.cs b/csharp/Facebook.CSSLayout/CSSNode.cs index 6d44995d..32fcc4a9 100644 --- a/csharp/Facebook.CSSLayout/CSSNode.cs +++ b/csharp/Facebook.CSSLayout/CSSNode.cs @@ -364,6 +364,19 @@ namespace Facebook.CSSLayout } } + public float StyleAspectRatio + { + get + { + return Native.CSSNodeStyleGetAspectRatio(_cssNode); + } + + set + { + Native.CSSNodeStyleSetAspectRatio(_cssNode, value); + } + } + public float LayoutX { get diff --git a/csharp/Facebook.CSSLayout/Native.cs b/csharp/Facebook.CSSLayout/Native.cs index d7c36449..682b8fdd 100644 --- a/csharp/Facebook.CSSLayout/Native.cs +++ b/csharp/Facebook.CSSLayout/Native.cs @@ -222,6 +222,12 @@ namespace Facebook.CSSLayout [DllImport(DllName)] public static extern float CSSNodeStyleGetMaxHeight(IntPtr node); + [DllImport(DllName)] + public static extern void CSSNodeStyleSetAspectRatio(IntPtr node, float aspectRatio); + + [DllImport(DllName)] + public static extern float CSSNodeStyleGetAspectRatio(IntPtr node); + #endregion #region CSS_NODE_STYLE_EDGE_PROPERTY diff --git a/java/com/facebook/csslayout/CSSNode.java b/java/com/facebook/csslayout/CSSNode.java index 95a5319e..d1943e9c 100644 --- a/java/com/facebook/csslayout/CSSNode.java +++ b/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/java/jni/CSSJNI.cpp b/java/jni/CSSJNI.cpp index 2c6ef3af..b5543390 100644 --- a/java/jni/CSSJNI.cpp +++ b/java/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/tests/CSSLayoutAspectRatioTest.cpp b/tests/CSSLayoutAspectRatioTest.cpp new file mode 100644 index 00000000..e43a7707 --- /dev/null +++ b/tests/CSSLayoutAspectRatioTest.cpp @@ -0,0 +1,407 @@ +/** + * Copyright (c) 2014-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. + */ + +#include +#include + +static CSSSize _measure(CSSNodeRef node, + float width, + CSSMeasureMode widthMode, + float height, + CSSMeasureMode heightMode) { + return CSSSize { + .width = widthMode == CSSMeasureModeExactly ? width : 50, + .height = heightMode == CSSMeasureModeExactly ? height : 50, + }; +} + +TEST(CSSLayoutTest, aspect_ratio_cross_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetWidth(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_main_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_both_dimensions_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_align_stretch) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_flex_grow) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetFlexGrow(root_child0, 1); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_flex_shrink) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 150); + CSSNodeStyleSetFlexShrink(root_child0, 1); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_basis) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetFlexBasis(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_absolute_layout_width_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetPositionType(root_child0, CSSPositionTypeAbsolute); + CSSNodeStyleSetPosition(root_child0, CSSEdgeLeft, 0); + CSSNodeStyleSetPosition(root_child0, CSSEdgeTop, 0); + CSSNodeStyleSetWidth(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_absolute_layout_height_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetPositionType(root_child0, CSSPositionTypeAbsolute); + CSSNodeStyleSetPosition(root_child0, CSSEdgeLeft, 0); + CSSNodeStyleSetPosition(root_child0, CSSEdgeTop, 0); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_with_max_cross_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetMaxWidth(root_child0, 40); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_with_max_main_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetWidth(root_child0, 50); + CSSNodeStyleSetMaxHeight(root_child0, 40); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(40, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_with_min_cross_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 30); + CSSNodeStyleSetMinWidth(root_child0, 40); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(30, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_with_min_main_defined) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetWidth(root_child0, 30); + CSSNodeStyleSetMinHeight(root_child0, 40); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(30, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(40, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_double_cross) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 2); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_half_cross) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetHeight(root_child0, 100); + CSSNodeStyleSetAspectRatio(root_child0, 0.5); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_double_main) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetWidth(root_child0, 50); + CSSNodeStyleSetAspectRatio(root_child0, 2); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_half_main) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeStyleSetWidth(root_child0, 100); + CSSNodeStyleSetAspectRatio(root_child0, 0.5); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +} + +TEST(CSSLayoutTest, aspect_ratio_with_measure_func) { + const CSSNodeRef root = CSSNodeNew(); + CSSNodeStyleSetAlignItems(root, CSSAlignFlexStart); + CSSNodeStyleSetWidth(root, 100); + CSSNodeStyleSetHeight(root, 100); + + const CSSNodeRef root_child0 = CSSNodeNew(); + CSSNodeSetMeasureFunc(root_child0, _measure); + CSSNodeStyleSetAspectRatio(root_child0, 1); + CSSNodeInsertChild(root, root_child0, 0); + + CSSNodeCalculateLayout(root, CSSUndefined, CSSUndefined, CSSDirectionLTR); + + ASSERT_EQ(0, CSSNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, CSSNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, CSSNodeLayoutGetHeight(root_child0)); + + CSSNodeFreeRecursive(root); +}