diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index 06b08c74..02e9399d 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -88,6 +88,14 @@ namespace Facebook.Yoga [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeRemoveChild(YGNodeHandle node, YGNodeHandle child); + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGNodeSetIsReferenceBaseline( + YGNodeHandle node, + bool isReferenceBaseline); + + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern bool YGNodeIsReferenceBaseline(YGNodeHandle node); + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeCalculateLayout( YGNodeHandle node, diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 57e7c7d9..b1f30b22 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -519,6 +519,19 @@ namespace Facebook.Yoga Native.YGNodeSetHasNewLayout(_ygNode, false); } + public bool IsReferenceBaseline + { + get + { + return Native.YGNodeIsReferenceBaseline(_ygNode); + } + + set + { + Native.YGNodeSetIsReferenceBaseline(_ygNode, value); + } + } + public bool ValuesEqual(float f1, float f2) { if (float.IsNaN(f1) || float.IsNaN(f2)) @@ -594,7 +607,7 @@ namespace Facebook.Yoga } public void CalculateLayout( - float width = YogaConstants.Undefined, + float width = YogaConstants.Undefined, float height = YogaConstants.Undefined) { Native.YGNodeCalculateLayout( diff --git a/java/com/facebook/yoga/YogaNode.java b/java/com/facebook/yoga/YogaNode.java index 282931dc..efbfc89b 100644 --- a/java/com/facebook/yoga/YogaNode.java +++ b/java/com/facebook/yoga/YogaNode.java @@ -159,6 +159,7 @@ public class YogaNode implements Cloneable { } private static native void jni_YGNodeInsertChild(long nativePointer, long childPointer, int index); + public void addChildAt(YogaNode child, int i) { if (child.mOwner != null) { throw new IllegalStateException("Child already has a parent, it must be removed first."); @@ -183,6 +184,18 @@ public class YogaNode implements Cloneable { jni_YGNodeInsertSharedChild(mNativePointer, child.mNativePointer, i); } + private static native void jni_YGNodeSetIsReferenceBaseline(long nativePointer, boolean isReferenceBaseline); + + public void setIsReferenceBaseline(boolean isReferenceBaseline) { + jni_YGNodeSetIsReferenceBaseline(mNativePointer, isReferenceBaseline); + } + + private static native boolean jni_YGNodeIsReferenceBaseline(long nativePointer); + + public boolean isReferenceBaseline() { + return jni_YGNodeIsReferenceBaseline(mNativePointer); + } + private static native void jni_YGNodeSetOwner(long nativePointer, long newOwnerNativePointer); private native long jni_YGNodeClone(long nativePointer, Object newNode); diff --git a/java/jni/YGJNI.cpp b/java/jni/YGJNI.cpp index d7854656..5d80a3d2 100644 --- a/java/jni/YGJNI.cpp +++ b/java/jni/YGJNI.cpp @@ -371,6 +371,17 @@ void jni_YGNodeRemoveChild(jlong nativePointer, jlong childPointer) { _jlong2YGNodeRef(nativePointer), _jlong2YGNodeRef(childPointer)); } +void jni_YGNodeSetIsReferenceBaseline( + jlong nativePointer, + jboolean isReferenceBaseline) { + YGNodeSetIsReferenceBaseline( + _jlong2YGNodeRef(nativePointer), isReferenceBaseline); +} + +jboolean jni_YGNodeIsReferenceBaseline(jlong nativePointer) { + return YGNodeIsReferenceBaseline(_jlong2YGNodeRef(nativePointer)); +} + void jni_YGNodeCalculateLayout( alias_ref, jlong nativePointer, @@ -666,6 +677,8 @@ jint jni_YGNodeGetInstanceCount() { YGMakeCriticalNativeMethod(jni_YGNodeInsertChild), \ YGMakeCriticalNativeMethod(jni_YGNodeInsertSharedChild), \ YGMakeCriticalNativeMethod(jni_YGNodeRemoveChild), \ + YGMakeCriticalNativeMethod(jni_YGNodeSetIsReferenceBaseline), \ + YGMakeCriticalNativeMethod(jni_YGNodeIsReferenceBaseline), \ YGMakeNativeMethod(jni_YGNodeCalculateLayout), \ YGMakeCriticalNativeMethod(jni_YGNodeMarkDirty), \ YGMakeCriticalNativeMethod( \ diff --git a/javascript/sources/Node.cc b/javascript/sources/Node.cc index a2305819..c684da28 100644 --- a/javascript/sources/Node.cc +++ b/javascript/sources/Node.cc @@ -253,6 +253,10 @@ void Node::setPaddingPercent(int edge, double padding) YGNodeStyleSetPaddingPercent(m_node, static_cast(edge), padding); } +void Node::setIsReferenceBaseline(bool isReferenceBaseline) { + YGNodeSetIsReferenceBaseline(m_node, isReferenceBaseline); +} + int Node::getPositionType(void) const { return YGNodeStyleGetPositionType(m_node); @@ -368,6 +372,10 @@ Value Node::getPadding(int edge) const return Value::fromYGValue(YGNodeStyleGetPadding(m_node, static_cast(edge))); } +bool Node::isReferenceBaseline() { + return YGNodeIsReferenceBaseline(m_node); +} + void Node::insertChild(Node * child, unsigned index) { YGNodeInsertChild(m_node, child->m_node, index); diff --git a/javascript/sources/Node.hh b/javascript/sources/Node.hh index ef9887e4..593745b2 100644 --- a/javascript/sources/Node.hh +++ b/javascript/sources/Node.hh @@ -187,7 +187,9 @@ class Node { double getComputedBorder(int edge) const; double getComputedPadding(int edge) const; - private: + public: + void setIsReferenceBaseline(bool isReferenceBaseline); + bool isReferenceBaseline(); YGNodeRef m_node; diff --git a/javascript/sources/nbind.cc b/javascript/sources/nbind.cc index 53ff0206..0ea7e0db 100644 --- a/javascript/sources/nbind.cc +++ b/javascript/sources/nbind.cc @@ -150,6 +150,9 @@ NBIND_CLASS(Node) method(getParent); method(getChild); + method(isReferenceBaseline); + method(setIsReferenceBaseline); + method(setMeasureFunc); method(unsetMeasureFunc); diff --git a/tests/YGAlignBaselineTest.cpp b/tests/YGAlignBaselineTest.cpp index 78179b98..0ede52bd 100644 --- a/tests/YGAlignBaselineTest.cpp +++ b/tests/YGAlignBaselineTest.cpp @@ -5,8 +5,8 @@ * file in the root directory of this source tree. * */ - #include +#include #include static float @@ -38,6 +38,22 @@ static YGSize _measure2( }; } +static YGNodeRef createYGNode( + YGConfigRef config, + YGFlexDirection direction, + int width, + int height, + bool alignBaseline) { + const YGNodeRef node = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(node, direction); + if (alignBaseline) { + YGNodeStyleSetAlignItems(node, YGAlignBaseline); + } + YGNodeStyleSetWidth(node, width); + YGNodeStyleSetHeight(node, height); + return node; +} + // Test case for bug in T32999822 TEST(YogaTest, align_baseline_parent_ht_not_specified) { YGConfigRef config = YGConfigNew(); @@ -164,3 +180,673 @@ TEST(YogaTest, align_baseline_with_no_baseline_func_and_no_parent_ht) { YGConfigFree(config); } + +TEST(YogaTest, align_baseline_parent_using_child_in_column_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_with_padding_in_column_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeLeft, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeRight, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeTop, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_with_padding_using_child_in_column_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeStyleSetPadding(root_child1, YGEdgeLeft, 100); + YGNodeStyleSetPadding(root_child1, YGEdgeRight, 100); + YGNodeStyleSetPadding(root_child1, YGEdgeTop, 100); + YGNodeStyleSetPadding(root_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_with_margin_using_child_in_column_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeStyleSetMargin(root_child1, YGEdgeLeft, 100); + YGNodeStyleSetMargin(root_child1, YGEdgeRight, 100); + YGNodeStyleSetMargin(root_child1, YGEdgeTop, 100); + YGNodeStyleSetMargin(root_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(600, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_with_margin_in_column_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeLeft, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeRight, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeTop, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_parent_using_child_in_row_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionRow, 500, 800, true); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_with_padding_in_row_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionRow, 500, 800, true); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeLeft, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeRight, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeTop, 100); + YGNodeStyleSetPadding(root_child1_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_with_margin_in_row_as_reference) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionRow, 500, 800, true); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeLeft, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeRight, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeTop, 100); + YGNodeStyleSetMargin(root_child1_child1, YGEdgeBottom, 100); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(600, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_column_as_reference_with_no_baseline_func) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 800, false); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_row_as_reference_with_no_baseline_func) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = + createYGNode(config, YGFlexDirectionRow, 1000, 1000, true); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = + createYGNode(config, YGFlexDirectionRow, 500, 800, true); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_column_as_reference_with_height_not_specified) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 1000); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root_child1, 500); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(800, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_row_as_reference_with_height_not_specified) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 1000); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionRow); + YGNodeStyleSetWidth(root_child1, 500); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + root_child1_child1->setBaseLineFunc(_baselineFunc); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(900, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_column_as_reference_with_no_baseline_func_and_height_not_specified) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 1000); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root_child1, 500); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 300, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST( + YogaTest, + align_baseline_parent_using_child_in_row_as_reference_with_no_baseline_func_and_height_not_specified) { + YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 1000); + + const YGNodeRef root_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 600, false); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionRow); + YGNodeStyleSetWidth(root_child1, 500); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = + createYGNode(config, YGFlexDirectionColumn, 500, 500, false); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = + createYGNode(config, YGFlexDirectionColumn, 500, 400, false); + YGNodeSetIsReferenceBaseline(root_child1_child1, true); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/yoga/YGNode.h b/yoga/YGNode.h index d771aad2..103b5076 100644 --- a/yoga/YGNode.h +++ b/yoga/YGNode.h @@ -17,6 +17,7 @@ struct YGNode { void* context_ = nullptr; YGPrintFunc print_ = nullptr; bool hasNewLayout_ = true; + bool isReferenceBaseline_ = false; YGNodeType nodeType_ = YGNodeTypeDefault; YGMeasureFunc measure_ = nullptr; YGBaselineFunc baseline_ = nullptr; @@ -93,6 +94,10 @@ struct YGNode { return lineIndex_; } + bool isReferenceBaseline() { + return isReferenceBaseline_; + } + // returns the YGNodeRef that owns this YGNode. An owner is used to identify // the YogaTree that a YGNode belongs to. // This method will return the parent of the YGNode when a YGNode only belongs @@ -211,6 +216,10 @@ struct YGNode { lineIndex_ = lineIndex; } + void setIsReferenceBaseline(bool isReferenceBaseline) { + isReferenceBaseline_ = isReferenceBaseline; + } + void setOwner(YGNodeRef owner) { owner_ = owner; } diff --git a/yoga/Yoga.cpp b/yoga/Yoga.cpp index 6e42d51a..8afe45ff 100644 --- a/yoga/Yoga.cpp +++ b/yoga/Yoga.cpp @@ -380,6 +380,14 @@ void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) { memcpy(dest, src, sizeof(YGConfig)); } +void YGNodeSetIsReferenceBaseline(YGNodeRef node, bool isReferenceBaseline) { + node->setIsReferenceBaseline(isReferenceBaseline); +} + +bool YGNodeIsReferenceBaseline(YGNodeRef node) { + return node->isReferenceBaseline(); +} + void YGNodeInsertChild( const YGNodeRef node, const YGNodeRef child, @@ -1138,7 +1146,8 @@ static float YGBaseline(const YGNodeRef node) { if (child->getStyle().positionType == YGPositionTypeAbsolute) { continue; } - if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + if (YGNodeAlignItem(node, child) == YGAlignBaseline || + child->isReferenceBaseline()) { baselineChild = child; break; } diff --git a/yoga/Yoga.h b/yoga/Yoga.h index d5da362d..dddab8bb 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -114,6 +114,12 @@ WIN_EXPORT void YGNodeSetChildren( const YGNodeRef children[], const uint32_t count); +WIN_EXPORT void YGNodeSetIsReferenceBaseline( + YGNodeRef node, + bool isReferenceBaseline); + +WIN_EXPORT bool YGNodeIsReferenceBaseline(YGNodeRef node); + WIN_EXPORT void YGNodeCalculateLayout( const YGNodeRef node, const float availableWidth,