From 2e4085cfda188abf78f104a9f88c70576c4c8792 Mon Sep 17 00:00:00 2001 From: Aleksey Zinchenko Date: Tue, 27 Feb 2018 15:11:27 +0800 Subject: [PATCH 1/9] Update yoga core to https://github.com/facebook/yoga/commit/295d111f0eafd9275bc4a9e8e646dbcdd5e88c2d, add core tests, their dependencies, update buck config --- .buckconfig | 2 + .gitignore | 8 + BUCK | 45 + Sources/yoga-tests/YGAbsolutePositionTest.cpp | 1031 +++ Sources/yoga-tests/YGAlignContentTest.cpp | 1878 ++++ Sources/yoga-tests/YGAlignItemsTest.cpp | 2158 +++++ Sources/yoga-tests/YGAlignSelfTest.cpp | 249 + Sources/yoga-tests/YGAndroidNewsFeed.cpp | 445 + Sources/yoga-tests/YGAspectRatioTest.cpp | 898 ++ Sources/yoga-tests/YGBaselineFuncTest.cpp | 62 + Sources/yoga-tests/YGBorderTest.cpp | 212 + Sources/yoga-tests/YGComputedMarginTest.cpp | 28 + Sources/yoga-tests/YGComputedPaddingTest.cpp | 28 + Sources/yoga-tests/YGDefaultValuesTest.cpp | 164 + Sources/yoga-tests/YGDimensionTest.cpp | 100 + Sources/yoga-tests/YGDirtiedTest.cpp | 107 + Sources/yoga-tests/YGDirtyMarkingTest.cpp | 159 + Sources/yoga-tests/YGDisplayTest.cpp | 332 + Sources/yoga-tests/YGEdgeTest.cpp | 161 + Sources/yoga-tests/YGFlexDirectionTest.cpp | 415 + Sources/yoga-tests/YGFlexTest.cpp | 491 ++ Sources/yoga-tests/YGFlexWrapTest.cpp | 1735 ++++ Sources/yoga-tests/YGHadOverflowTest.cpp | 127 + Sources/yoga-tests/YGInfiniteHeightTest.cpp | 53 + Sources/yoga-tests/YGJustifyContentTest.cpp | 999 +++ Sources/yoga-tests/YGLayoutDiffingTest.cpp | 87 + Sources/yoga-tests/YGLoggerTest.cpp | 87 + Sources/yoga-tests/YGMarginTest.cpp | 1717 ++++ Sources/yoga-tests/YGMeasureCacheTest.cpp | 176 + Sources/yoga-tests/YGMeasureModeTest.cpp | 326 + Sources/yoga-tests/YGMeasureTest.cpp | 689 ++ Sources/yoga-tests/YGMinMaxDimensionTest.cpp | 1297 +++ Sources/yoga-tests/YGNodeChildTest.cpp | 34 + Sources/yoga-tests/YGPaddingTest.cpp | 258 + Sources/yoga-tests/YGPercentageTest.cpp | 1194 +++ Sources/yoga-tests/YGPersistenceTest.cpp | 250 + Sources/yoga-tests/YGRelayoutTest.cpp | 50 + Sources/yoga-tests/YGRoundingFunctionTest.cpp | 28 + .../yoga-tests/YGRoundingMeasureFuncTest.cpp | 130 + Sources/yoga-tests/YGRoundingTest.cpp | 1077 +++ Sources/yoga-tests/YGSizeOverflowTest.cpp | 173 + Sources/yoga-tests/YGStyleTest.cpp | 59 + .../YGZeroOutLayoutRecursivlyTest.cpp | 37 + Sources/yoga/Utils.cpp | 29 + Sources/yoga/Utils.h | 102 + Sources/yoga/{YGEnums.c => YGEnums.cpp} | 9 +- Sources/yoga/YGEnums.h | 21 +- Sources/yoga/YGLayout.cpp | 66 + Sources/yoga/YGLayout.h | 42 + Sources/yoga/YGMacros.h | 10 +- Sources/yoga/YGNode.cpp | 743 ++ Sources/yoga/YGNode.h | 163 + Sources/yoga/YGNodeList.c | 104 - Sources/yoga/YGNodeList.h | 33 - Sources/yoga/YGNodePrint.cpp | 225 + Sources/yoga/YGNodePrint.h | 22 + Sources/yoga/YGStyle.cpp | 99 + Sources/yoga/YGStyle.h | 43 + Sources/yoga/Yoga-internal.h | 109 + Sources/yoga/Yoga.c | 3673 -------- Sources/yoga/Yoga.cpp | 3764 ++++++++ Sources/yoga/Yoga.h | 65 +- lib/fb/BUCK | 44 + lib/fb/build.gradle | 33 + lib/fb/src/main/AndroidManifest.xml | 12 + lib/fb/src/main/cpp/CMakeLists.txt | 31 + lib/fb/src/main/cpp/Doxyfile | 15 + lib/fb/src/main/cpp/assert.cpp | 39 + lib/fb/src/main/cpp/include/fb/ALog.h | 81 + lib/fb/src/main/cpp/include/fb/Countable.h | 45 + lib/fb/src/main/cpp/include/fb/Doxyfile | 18 + lib/fb/src/main/cpp/include/fb/Environment.h | 75 + .../src/main/cpp/include/fb/ProgramLocation.h | 48 + lib/fb/src/main/cpp/include/fb/RefPtr.h | 272 + .../main/cpp/include/fb/StaticInitialized.h | 38 + lib/fb/src/main/cpp/include/fb/ThreadLocal.h | 116 + lib/fb/src/main/cpp/include/fb/assert.h | 34 + lib/fb/src/main/cpp/include/fb/fbjni.h | 22 + lib/fb/src/main/cpp/include/fb/fbjni/Boxed.h | 64 + .../main/cpp/include/fb/fbjni/ByteBuffer.h | 32 + lib/fb/src/main/cpp/include/fb/fbjni/Common.h | 104 + .../src/main/cpp/include/fb/fbjni/Context.h | 33 + .../cpp/include/fb/fbjni/CoreClasses-inl.h | 661 ++ .../main/cpp/include/fb/fbjni/CoreClasses.h | 593 ++ .../main/cpp/include/fb/fbjni/Exceptions.h | 116 + lib/fb/src/main/cpp/include/fb/fbjni/File.h | 28 + lib/fb/src/main/cpp/include/fb/fbjni/Hybrid.h | 231 + .../main/cpp/include/fb/fbjni/Iterator-inl.h | 197 + .../src/main/cpp/include/fb/fbjni/Iterator.h | 144 + .../src/main/cpp/include/fb/fbjni/JThread.h | 37 + .../main/cpp/include/fb/fbjni/Meta-forward.h | 34 + .../src/main/cpp/include/fb/fbjni/Meta-inl.h | 430 + lib/fb/src/main/cpp/include/fb/fbjni/Meta.h | 338 + .../main/cpp/include/fb/fbjni/MetaConvert.h | 122 + .../cpp/include/fb/fbjni/NativeRunnable.h | 45 + .../fb/fbjni/ReferenceAllocators-inl.h | 118 + .../include/fb/fbjni/ReferenceAllocators.h | 60 + .../cpp/include/fb/fbjni/References-forward.h | 65 + .../cpp/include/fb/fbjni/References-inl.h | 508 ++ .../main/cpp/include/fb/fbjni/References.h | 586 ++ .../cpp/include/fb/fbjni/Registration-inl.h | 184 + .../main/cpp/include/fb/fbjni/Registration.h | 85 + .../main/cpp/include/fb/fbjni/TypeTraits.h | 158 + lib/fb/src/main/cpp/include/fb/log.h | 346 + lib/fb/src/main/cpp/include/fb/lyra.h | 168 + lib/fb/src/main/cpp/include/fb/noncopyable.h | 19 + lib/fb/src/main/cpp/include/fb/nonmovable.h | 19 + lib/fb/src/main/cpp/include/fb/visibility.h | 10 + lib/fb/src/main/cpp/include/jni/Countable.h | 34 + .../main/cpp/include/jni/GlobalReference.h | 89 + .../src/main/cpp/include/jni/LocalReference.h | 35 + lib/fb/src/main/cpp/include/jni/LocalString.h | 92 + .../src/main/cpp/include/jni/Registration.h | 25 + .../src/main/cpp/include/jni/WeakReference.h | 53 + lib/fb/src/main/cpp/include/jni/jni_helpers.h | 137 + lib/fb/src/main/cpp/jni/ByteBuffer.cpp | 75 + lib/fb/src/main/cpp/jni/Countable.cpp | 67 + lib/fb/src/main/cpp/jni/Environment.cpp | 130 + lib/fb/src/main/cpp/jni/Exceptions.cpp | 283 + lib/fb/src/main/cpp/jni/Hybrid.cpp | 63 + lib/fb/src/main/cpp/jni/LocalString.cpp | 310 + lib/fb/src/main/cpp/jni/OnLoad.cpp | 20 + lib/fb/src/main/cpp/jni/References.cpp | 39 + lib/fb/src/main/cpp/jni/WeakReference.cpp | 41 + lib/fb/src/main/cpp/jni/fbjni.cpp | 194 + lib/fb/src/main/cpp/jni/jni_helpers.cpp | 195 + lib/fb/src/main/cpp/log.cpp | 98 + lib/fb/src/main/cpp/lyra/lyra.cpp | 120 + lib/fb/src/main/cpp/onload.cpp | 29 + lib/fb/src/main/java/com/facebook/jni/BUCK | 20 + .../main/java/com/facebook/jni/Countable.java | 30 + .../java/com/facebook/jni/CppException.java | 18 + .../facebook/jni/CppSystemErrorException.java | 25 + .../java/com/facebook/jni/HybridData.java | 44 + .../java/com/facebook/jni/IteratorHelper.java | 54 + .../com/facebook/jni/MapIteratorHelper.java | 51 + .../java/com/facebook/jni/NativeRunnable.java | 26 + .../com/facebook/jni/ThreadScopeSupport.java | 17 + .../com/facebook/jni/UnknownCppException.java | 23 + .../src/main/java/com/facebook/jni/fbjni.pro | 11 + lib/gtest/BUCK | 25 + lib/gtest/googletest/.gitignore | 2 + lib/gtest/googletest/.travis.yml | 46 + lib/gtest/googletest/CMakeLists.txt | 16 + lib/gtest/googletest/README.md | 142 + lib/gtest/googletest/appveyor.yml | 71 + lib/gtest/googletest/googlemock/CHANGES | 126 + .../googletest/googlemock/CMakeLists.txt | 202 + lib/gtest/googletest/googlemock/CONTRIBUTORS | 40 + lib/gtest/googletest/googlemock/LICENSE | 28 + lib/gtest/googletest/googlemock/Makefile.am | 224 + lib/gtest/googletest/googlemock/README.md | 333 + .../googletest/googlemock/build-aux/.keep | 0 lib/gtest/googletest/googlemock/configure.ac | 146 + .../googletest/googlemock/docs/CheatSheet.md | 562 ++ .../googletest/googlemock/docs/CookBook.md | 3675 ++++++++ .../googletest/googlemock/docs/DesignDoc.md | 280 + .../googletest/googlemock/docs/DevGuide.md | 132 + .../googlemock/docs/Documentation.md | 12 + .../googletest/googlemock/docs/ForDummies.md | 439 + .../docs/FrequentlyAskedQuestions.md | 628 ++ .../googletest/googlemock/docs/KnownIssues.md | 19 + .../googlemock/docs/v1_5/CheatSheet.md | 525 ++ .../googlemock/docs/v1_5/CookBook.md | 3250 +++++++ .../googlemock/docs/v1_5/Documentation.md | 11 + .../googlemock/docs/v1_5/ForDummies.md | 439 + .../docs/v1_5/FrequentlyAskedQuestions.md | 624 ++ .../googlemock/docs/v1_6/CheatSheet.md | 534 ++ .../googlemock/docs/v1_6/CookBook.md | 3342 +++++++ .../googlemock/docs/v1_6/Documentation.md | 12 + .../googlemock/docs/v1_6/ForDummies.md | 439 + .../docs/v1_6/FrequentlyAskedQuestions.md | 628 ++ .../googlemock/docs/v1_7/CheatSheet.md | 556 ++ .../googlemock/docs/v1_7/CookBook.md | 3432 ++++++++ .../googlemock/docs/v1_7/Documentation.md | 12 + .../googlemock/docs/v1_7/ForDummies.md | 439 + .../docs/v1_7/FrequentlyAskedQuestions.md | 628 ++ .../googlemock/include/gmock/gmock-actions.h | 1205 +++ .../include/gmock/gmock-cardinalities.h | 147 + .../include/gmock/gmock-generated-actions.h | 2377 +++++ .../gmock/gmock-generated-actions.h.pump | 794 ++ .../gmock/gmock-generated-function-mockers.h | 1095 +++ .../gmock-generated-function-mockers.h.pump | 291 + .../include/gmock/gmock-generated-matchers.h | 2179 +++++ .../gmock/gmock-generated-matchers.h.pump | 672 ++ .../gmock/gmock-generated-nice-strict.h | 397 + .../gmock/gmock-generated-nice-strict.h.pump | 161 + .../googlemock/include/gmock/gmock-matchers.h | 4399 ++++++++++ .../include/gmock/gmock-more-actions.h | 246 + .../include/gmock/gmock-more-matchers.h | 58 + .../include/gmock/gmock-spec-builders.h | 1847 ++++ .../googlemock/include/gmock/gmock.h | 94 + .../internal/custom/gmock-generated-actions.h | 8 + .../custom/gmock-generated-actions.h.pump | 10 + .../gmock/internal/custom/gmock-matchers.h | 39 + .../gmock/internal/custom/gmock-port.h | 46 + .../internal/gmock-generated-internal-utils.h | 279 + .../gmock-generated-internal-utils.h.pump | 136 + .../gmock/internal/gmock-internal-utils.h | 511 ++ .../include/gmock/internal/gmock-port.h | 91 + lib/gtest/googletest/googlemock/make/Makefile | 101 + .../googletest/googlemock/msvc/2005/gmock.sln | 32 + .../googlemock/msvc/2005/gmock.vcproj | 191 + .../googlemock/msvc/2005/gmock_config.vsprops | 15 + .../googlemock/msvc/2005/gmock_main.vcproj | 187 + .../googlemock/msvc/2005/gmock_test.vcproj | 201 + .../googletest/googlemock/msvc/2010/gmock.sln | 32 + .../googlemock/msvc/2010/gmock.vcxproj | 82 + .../googlemock/msvc/2010/gmock_config.props | 19 + .../googlemock/msvc/2010/gmock_main.vcxproj | 88 + .../googlemock/msvc/2010/gmock_test.vcxproj | 101 + .../googletest/googlemock/msvc/2015/gmock.sln | 32 + .../googlemock/msvc/2015/gmock.vcxproj | 84 + .../googlemock/msvc/2015/gmock_config.props | 19 + .../googlemock/msvc/2015/gmock_main.vcxproj | 90 + .../googlemock/msvc/2015/gmock_test.vcxproj | 103 + .../googlemock/scripts/fuse_gmock_files.py | 240 + .../googlemock/scripts/generator/LICENSE | 203 + .../googlemock/scripts/generator/README | 35 + .../scripts/generator/README.cppclean | 115 + .../scripts/generator/cpp/__init__.py | 0 .../googlemock/scripts/generator/cpp/ast.py | 1733 ++++ .../scripts/generator/cpp/gmock_class.py | 227 + .../scripts/generator/cpp/gmock_class_test.py | 448 + .../scripts/generator/cpp/keywords.py | 59 + .../scripts/generator/cpp/tokenize.py | 287 + .../googlemock/scripts/generator/cpp/utils.py | 41 + .../googlemock/scripts/generator/gmock_gen.py | 31 + .../googlemock/scripts/gmock-config.in | 303 + .../googlemock/scripts/gmock_doctor.py | 640 ++ .../googletest/googlemock/scripts/upload.py | 1387 +++ .../googlemock/scripts/upload_gmock.py | 78 + .../googletest/googlemock/src/gmock-all.cc | 47 + .../googlemock/src/gmock-cardinalities.cc | 156 + .../googlemock/src/gmock-internal-utils.cc | 174 + .../googlemock/src/gmock-matchers.cc | 498 ++ .../googlemock/src/gmock-spec-builders.cc | 823 ++ lib/gtest/googletest/googlemock/src/gmock.cc | 183 + .../googletest/googlemock/src/gmock_main.cc | 54 + .../googlemock/test/gmock-actions_test.cc | 1411 +++ .../test/gmock-cardinalities_test.cc | 428 + .../test/gmock-generated-actions_test.cc | 1228 +++ .../gmock-generated-function-mockers_test.cc | 622 ++ .../gmock-generated-internal-utils_test.cc | 127 + .../test/gmock-generated-matchers_test.cc | 1286 +++ .../test/gmock-internal-utils_test.cc | 699 ++ .../googlemock/test/gmock-matchers_test.cc | 5652 ++++++++++++ .../test/gmock-more-actions_test.cc | 708 ++ .../googlemock/test/gmock-nice-strict_test.cc | 424 + .../googlemock/test/gmock-port_test.cc | 43 + .../test/gmock-spec-builders_test.cc | 2644 ++++++ .../googlemock/test/gmock_all_test.cc | 51 + .../googlemock/test/gmock_ex_test.cc | 81 + .../googlemock/test/gmock_leak_test.py | 108 + .../googlemock/test/gmock_leak_test_.cc | 100 + .../googlemock/test/gmock_link2_test.cc | 40 + .../googlemock/test/gmock_link_test.cc | 40 + .../googlemock/test/gmock_link_test.h | 669 ++ .../googlemock/test/gmock_output_test.py | 180 + .../googlemock/test/gmock_output_test_.cc | 291 + .../test/gmock_output_test_golden.txt | 310 + .../googlemock/test/gmock_stress_test.cc | 322 + .../googletest/googlemock/test/gmock_test.cc | 220 + .../googlemock/test/gmock_test_utils.py | 112 + lib/gtest/googletest/googletest/.gitignore | 2 + lib/gtest/googletest/googletest/CHANGES | 157 + .../googletest/googletest/CMakeLists.txt | 286 + lib/gtest/googletest/googletest/CONTRIBUTORS | 37 + lib/gtest/googletest/googletest/LICENSE | 28 + lib/gtest/googletest/googletest/Makefile.am | 310 + lib/gtest/googletest/googletest/README.md | 280 + .../googletest/googletest/build-aux/.keep | 0 .../googletest/cmake/internal_utils.cmake | 256 + .../googletest/codegear/gtest.cbproj | 138 + .../googletest/codegear/gtest.groupproj | 54 + .../googletest/codegear/gtest_all.cc | 38 + .../googletest/codegear/gtest_link.cc | 40 + .../googletest/codegear/gtest_main.cbproj | 82 + .../googletest/codegear/gtest_unittest.cbproj | 88 + lib/gtest/googletest/googletest/configure.ac | 68 + .../googletest/docs/AdvancedGuide.md | 2182 +++++ .../googletest/googletest/docs/DevGuide.md | 126 + .../googletest/docs/Documentation.md | 14 + lib/gtest/googletest/googletest/docs/FAQ.md | 1087 +++ .../googletest/googletest/docs/Primer.md | 502 ++ .../googletest/googletest/docs/PumpManual.md | 177 + .../googletest/googletest/docs/Samples.md | 14 + .../googletest/docs/V1_5_AdvancedGuide.md | 2096 +++++ .../googletest/docs/V1_5_Documentation.md | 12 + .../googletest/googletest/docs/V1_5_FAQ.md | 886 ++ .../googletest/googletest/docs/V1_5_Primer.md | 497 ++ .../googletest/docs/V1_5_PumpManual.md | 177 + .../googletest/docs/V1_5_XcodeGuide.md | 93 + .../googletest/docs/V1_6_AdvancedGuide.md | 2178 +++++ .../googletest/docs/V1_6_Documentation.md | 14 + .../googletest/googletest/docs/V1_6_FAQ.md | 1038 +++ .../googletest/googletest/docs/V1_6_Primer.md | 501 ++ .../googletest/docs/V1_6_PumpManual.md | 177 + .../googletest/docs/V1_6_Samples.md | 14 + .../googletest/docs/V1_6_XcodeGuide.md | 93 + .../googletest/docs/V1_7_AdvancedGuide.md | 2181 +++++ .../googletest/docs/V1_7_Documentation.md | 14 + .../googletest/googletest/docs/V1_7_FAQ.md | 1082 +++ .../googletest/googletest/docs/V1_7_Primer.md | 501 ++ .../googletest/docs/V1_7_PumpManual.md | 177 + .../googletest/docs/V1_7_Samples.md | 14 + .../googletest/docs/V1_7_XcodeGuide.md | 93 + .../googletest/googletest/docs/XcodeGuide.md | 93 + .../include/gtest/gtest-death-test.h | 294 + .../googletest/include/gtest/gtest-message.h | 250 + .../include/gtest/gtest-param-test.h | 1444 +++ .../include/gtest/gtest-param-test.h.pump | 510 ++ .../googletest/include/gtest/gtest-printers.h | 993 +++ .../googletest/include/gtest/gtest-spi.h | 232 + .../include/gtest/gtest-test-part.h | 179 + .../include/gtest/gtest-typed-test.h | 263 + .../googletest/include/gtest/gtest.h | 2236 +++++ .../include/gtest/gtest_pred_impl.h | 358 + .../googletest/include/gtest/gtest_prod.h | 58 + .../gtest/internal/custom/gtest-port.h | 69 + .../gtest/internal/custom/gtest-printers.h | 42 + .../include/gtest/internal/custom/gtest.h | 41 + .../internal/gtest-death-test-internal.h | 319 + .../include/gtest/internal/gtest-filepath.h | 206 + .../include/gtest/internal/gtest-internal.h | 1238 +++ .../include/gtest/internal/gtest-linked_ptr.h | 243 + .../internal/gtest-param-util-generated.h | 5146 +++++++++++ .../gtest-param-util-generated.h.pump | 286 + .../include/gtest/internal/gtest-param-util.h | 731 ++ .../include/gtest/internal/gtest-port-arch.h | 93 + .../include/gtest/internal/gtest-port.h | 2564 ++++++ .../include/gtest/internal/gtest-string.h | 167 + .../include/gtest/internal/gtest-tuple.h | 1020 +++ .../include/gtest/internal/gtest-tuple.h.pump | 347 + .../include/gtest/internal/gtest-type-util.h | 3331 +++++++ .../gtest/internal/gtest-type-util.h.pump | 297 + .../googletest/googletest/m4/acx_pthread.m4 | 363 + lib/gtest/googletest/googletest/m4/gtest.m4 | 74 + lib/gtest/googletest/googletest/make/Makefile | 82 + .../googletest/googletest/msvc/gtest-md.sln | 45 + .../googletest/msvc/gtest-md.vcproj | 126 + .../googletest/googletest/msvc/gtest.sln | 45 + .../googletest/googletest/msvc/gtest.vcproj | 126 + .../googletest/msvc/gtest_main-md.vcproj | 129 + .../googletest/msvc/gtest_main.vcproj | 129 + .../googletest/msvc/gtest_prod_test-md.vcproj | 164 + .../googletest/msvc/gtest_prod_test.vcproj | 164 + .../googletest/msvc/gtest_unittest-md.vcproj | 147 + .../googletest/msvc/gtest_unittest.vcproj | 147 + .../googletest/samples/prime_tables.h | 123 + .../googletest/googletest/samples/sample1.cc | 68 + .../googletest/googletest/samples/sample1.h | 43 + .../googletest/samples/sample10_unittest.cc | 144 + .../googletest/samples/sample1_unittest.cc | 153 + .../googletest/googletest/samples/sample2.cc | 56 + .../googletest/googletest/samples/sample2.h | 85 + .../googletest/samples/sample2_unittest.cc | 109 + .../googletest/samples/sample3-inl.h | 172 + .../googletest/samples/sample3_unittest.cc | 151 + .../googletest/googletest/samples/sample4.cc | 46 + .../googletest/googletest/samples/sample4.h | 53 + .../googletest/samples/sample4_unittest.cc | 45 + .../googletest/samples/sample5_unittest.cc | 199 + .../googletest/samples/sample6_unittest.cc | 224 + .../googletest/samples/sample7_unittest.cc | 130 + .../googletest/samples/sample8_unittest.cc | 173 + .../googletest/samples/sample9_unittest.cc | 160 + .../googletest/googletest/scripts/common.py | 83 + .../googletest/scripts/fuse_gtest_files.py | 253 + .../googletest/scripts/gen_gtest_pred_impl.py | 730 ++ .../googletest/scripts/gtest-config.in | 274 + .../googletest/googletest/scripts/pump.py | 855 ++ .../googletest/scripts/release_docs.py | 158 + .../googletest/scripts/test/Makefile | 59 + .../googletest/googletest/scripts/upload.py | 1387 +++ .../googletest/scripts/upload_gtest.py | 78 + .../googletest/googletest/src/gtest-all.cc | 48 + .../googletest/src/gtest-death-test.cc | 1342 +++ .../googletest/src/gtest-filepath.cc | 387 + .../googletest/src/gtest-internal-inl.h | 1183 +++ .../googletest/googletest/src/gtest-port.cc | 1259 +++ .../googletest/src/gtest-printers.cc | 373 + .../googletest/src/gtest-test-part.cc | 110 + .../googletest/src/gtest-typed-test.cc | 118 + lib/gtest/googletest/googletest/src/gtest.cc | 5388 ++++++++++++ .../googletest/googletest/src/gtest_main.cc | 38 + .../test/gtest-death-test_ex_test.cc | 93 + .../googletest/test/gtest-death-test_test.cc | 1427 +++ .../googletest/test/gtest-filepath_test.cc | 662 ++ .../googletest/test/gtest-linked_ptr_test.cc | 154 + .../googletest/test/gtest-listener_test.cc | 311 + .../googletest/test/gtest-message_test.cc | 159 + .../googletest/test/gtest-options_test.cc | 215 + .../googletest/test/gtest-param-test2_test.cc | 65 + .../googletest/test/gtest-param-test_test.cc | 1055 +++ .../googletest/test/gtest-param-test_test.h | 57 + .../googletest/test/gtest-port_test.cc | 1311 +++ .../googletest/test/gtest-printers_test.cc | 1637 ++++ .../googletest/test/gtest-test-part_test.cc | 208 + .../googletest/test/gtest-tuple_test.cc | 320 + .../googletest/test/gtest-typed-test2_test.cc | 45 + .../googletest/test/gtest-typed-test_test.cc | 380 + .../googletest/test/gtest-typed-test_test.h | 66 + .../test/gtest-unittest-api_test.cc | 341 + .../googletest/test/gtest_all_test.cc | 47 + .../test/gtest_break_on_failure_unittest.py | 212 + .../test/gtest_break_on_failure_unittest_.cc | 88 + .../test/gtest_catch_exceptions_test.py | 237 + .../test/gtest_catch_exceptions_test_.cc | 311 + .../googletest/test/gtest_color_test.py | 130 + .../googletest/test/gtest_color_test_.cc | 71 + .../googletest/test/gtest_env_var_test.py | 117 + .../googletest/test/gtest_env_var_test_.cc | 126 + .../googletest/test/gtest_environment_test.cc | 192 + .../googletest/test/gtest_filter_unittest.py | 636 ++ .../googletest/test/gtest_filter_unittest_.cc | 140 + .../googletest/test/gtest_help_test.py | 172 + .../googletest/test/gtest_help_test_.cc | 46 + .../test/gtest_list_tests_unittest.py | 207 + .../test/gtest_list_tests_unittest_.cc | 157 + .../googletest/test/gtest_main_unittest.cc | 45 + .../googletest/test/gtest_no_test_unittest.cc | 56 + .../googletest/test/gtest_output_test.py | 340 + .../googletest/test/gtest_output_test_.cc | 1062 +++ .../test/gtest_output_test_golden_lin.txt | 743 ++ .../test/gtest_pred_impl_unittest.cc | 2427 ++++++ .../test/gtest_premature_exit_test.cc | 127 + .../googletest/test/gtest_prod_test.cc | 57 + .../googletest/test/gtest_repeat_test.cc | 253 + .../googletest/test/gtest_shuffle_test.py | 325 + .../googletest/test/gtest_shuffle_test_.cc | 103 + .../googletest/test/gtest_sole_header_test.cc | 57 + .../googletest/test/gtest_stress_test.cc | 256 + .../googletest/test/gtest_test_utils.py | 320 + .../test/gtest_throw_on_failure_ex_test.cc | 92 + .../test/gtest_throw_on_failure_test.py | 171 + .../test/gtest_throw_on_failure_test_.cc | 72 + .../test/gtest_uninitialized_test.py | 70 + .../test/gtest_uninitialized_test_.cc | 43 + .../googletest/test/gtest_unittest.cc | 7706 +++++++++++++++++ .../test/gtest_xml_outfile1_test_.cc | 49 + .../test/gtest_xml_outfile2_test_.cc | 49 + .../test/gtest_xml_outfiles_test.py | 132 + .../test/gtest_xml_output_unittest.py | 308 + .../test/gtest_xml_output_unittest_.cc | 181 + .../googletest/test/gtest_xml_test_utils.py | 194 + .../googletest/googletest/test/production.cc | 36 + .../googletest/googletest/test/production.h | 55 + .../xcode/Config/DebugProject.xcconfig | 30 + .../xcode/Config/FrameworkTarget.xcconfig | 17 + .../googletest/xcode/Config/General.xcconfig | 41 + .../xcode/Config/ReleaseProject.xcconfig | 32 + .../xcode/Config/StaticLibraryTarget.xcconfig | 18 + .../xcode/Config/TestTarget.xcconfig | 8 + .../googletest/xcode/Resources/Info.plist | 30 + .../xcode/Samples/FrameworkSample/Info.plist | 28 + .../WidgetFramework.xcodeproj/project.pbxproj | 457 + .../xcode/Samples/FrameworkSample/runtests.sh | 62 + .../xcode/Samples/FrameworkSample/widget.cc | 63 + .../xcode/Samples/FrameworkSample/widget.h | 59 + .../Samples/FrameworkSample/widget_test.cc | 68 + .../googletest/xcode/Scripts/runtests.sh | 65 + .../xcode/Scripts/versiongenerate.py | 100 + .../xcode/gtest.xcodeproj/project.pbxproj | 1135 +++ lib/gtest/googletest/travis.sh | 15 + yoga_defs.bzl | 115 + 466 files changed, 187883 insertions(+), 3856 deletions(-) create mode 100644 .buckconfig create mode 100644 BUCK create mode 100644 Sources/yoga-tests/YGAbsolutePositionTest.cpp create mode 100644 Sources/yoga-tests/YGAlignContentTest.cpp create mode 100644 Sources/yoga-tests/YGAlignItemsTest.cpp create mode 100644 Sources/yoga-tests/YGAlignSelfTest.cpp create mode 100644 Sources/yoga-tests/YGAndroidNewsFeed.cpp create mode 100644 Sources/yoga-tests/YGAspectRatioTest.cpp create mode 100644 Sources/yoga-tests/YGBaselineFuncTest.cpp create mode 100644 Sources/yoga-tests/YGBorderTest.cpp create mode 100644 Sources/yoga-tests/YGComputedMarginTest.cpp create mode 100644 Sources/yoga-tests/YGComputedPaddingTest.cpp create mode 100644 Sources/yoga-tests/YGDefaultValuesTest.cpp create mode 100644 Sources/yoga-tests/YGDimensionTest.cpp create mode 100644 Sources/yoga-tests/YGDirtiedTest.cpp create mode 100644 Sources/yoga-tests/YGDirtyMarkingTest.cpp create mode 100644 Sources/yoga-tests/YGDisplayTest.cpp create mode 100644 Sources/yoga-tests/YGEdgeTest.cpp create mode 100644 Sources/yoga-tests/YGFlexDirectionTest.cpp create mode 100644 Sources/yoga-tests/YGFlexTest.cpp create mode 100644 Sources/yoga-tests/YGFlexWrapTest.cpp create mode 100644 Sources/yoga-tests/YGHadOverflowTest.cpp create mode 100644 Sources/yoga-tests/YGInfiniteHeightTest.cpp create mode 100644 Sources/yoga-tests/YGJustifyContentTest.cpp create mode 100644 Sources/yoga-tests/YGLayoutDiffingTest.cpp create mode 100644 Sources/yoga-tests/YGLoggerTest.cpp create mode 100644 Sources/yoga-tests/YGMarginTest.cpp create mode 100644 Sources/yoga-tests/YGMeasureCacheTest.cpp create mode 100644 Sources/yoga-tests/YGMeasureModeTest.cpp create mode 100644 Sources/yoga-tests/YGMeasureTest.cpp create mode 100644 Sources/yoga-tests/YGMinMaxDimensionTest.cpp create mode 100644 Sources/yoga-tests/YGNodeChildTest.cpp create mode 100644 Sources/yoga-tests/YGPaddingTest.cpp create mode 100644 Sources/yoga-tests/YGPercentageTest.cpp create mode 100644 Sources/yoga-tests/YGPersistenceTest.cpp create mode 100644 Sources/yoga-tests/YGRelayoutTest.cpp create mode 100644 Sources/yoga-tests/YGRoundingFunctionTest.cpp create mode 100644 Sources/yoga-tests/YGRoundingMeasureFuncTest.cpp create mode 100644 Sources/yoga-tests/YGRoundingTest.cpp create mode 100644 Sources/yoga-tests/YGSizeOverflowTest.cpp create mode 100644 Sources/yoga-tests/YGStyleTest.cpp create mode 100644 Sources/yoga-tests/YGZeroOutLayoutRecursivlyTest.cpp create mode 100644 Sources/yoga/Utils.cpp create mode 100644 Sources/yoga/Utils.h rename Sources/yoga/{YGEnums.c => YGEnums.cpp} (94%) create mode 100644 Sources/yoga/YGLayout.cpp create mode 100644 Sources/yoga/YGLayout.h create mode 100644 Sources/yoga/YGNode.cpp create mode 100644 Sources/yoga/YGNode.h delete mode 100644 Sources/yoga/YGNodeList.c delete mode 100644 Sources/yoga/YGNodeList.h create mode 100644 Sources/yoga/YGNodePrint.cpp create mode 100644 Sources/yoga/YGNodePrint.h create mode 100644 Sources/yoga/YGStyle.cpp create mode 100644 Sources/yoga/YGStyle.h create mode 100644 Sources/yoga/Yoga-internal.h delete mode 100644 Sources/yoga/Yoga.c create mode 100644 Sources/yoga/Yoga.cpp create mode 100644 lib/fb/BUCK create mode 100644 lib/fb/build.gradle create mode 100644 lib/fb/src/main/AndroidManifest.xml create mode 100644 lib/fb/src/main/cpp/CMakeLists.txt create mode 100644 lib/fb/src/main/cpp/Doxyfile create mode 100644 lib/fb/src/main/cpp/assert.cpp create mode 100644 lib/fb/src/main/cpp/include/fb/ALog.h create mode 100644 lib/fb/src/main/cpp/include/fb/Countable.h create mode 100644 lib/fb/src/main/cpp/include/fb/Doxyfile create mode 100644 lib/fb/src/main/cpp/include/fb/Environment.h create mode 100644 lib/fb/src/main/cpp/include/fb/ProgramLocation.h create mode 100644 lib/fb/src/main/cpp/include/fb/RefPtr.h create mode 100644 lib/fb/src/main/cpp/include/fb/StaticInitialized.h create mode 100644 lib/fb/src/main/cpp/include/fb/ThreadLocal.h create mode 100644 lib/fb/src/main/cpp/include/fb/assert.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Boxed.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/ByteBuffer.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Common.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Context.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Exceptions.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/File.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Hybrid.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Iterator-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Iterator.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/JThread.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Meta-forward.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Meta-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Meta.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/MetaConvert.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/NativeRunnable.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/References-forward.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/References-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/References.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/Registration.h create mode 100644 lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h create mode 100644 lib/fb/src/main/cpp/include/fb/log.h create mode 100644 lib/fb/src/main/cpp/include/fb/lyra.h create mode 100644 lib/fb/src/main/cpp/include/fb/noncopyable.h create mode 100644 lib/fb/src/main/cpp/include/fb/nonmovable.h create mode 100644 lib/fb/src/main/cpp/include/fb/visibility.h create mode 100644 lib/fb/src/main/cpp/include/jni/Countable.h create mode 100644 lib/fb/src/main/cpp/include/jni/GlobalReference.h create mode 100644 lib/fb/src/main/cpp/include/jni/LocalReference.h create mode 100644 lib/fb/src/main/cpp/include/jni/LocalString.h create mode 100644 lib/fb/src/main/cpp/include/jni/Registration.h create mode 100644 lib/fb/src/main/cpp/include/jni/WeakReference.h create mode 100644 lib/fb/src/main/cpp/include/jni/jni_helpers.h create mode 100644 lib/fb/src/main/cpp/jni/ByteBuffer.cpp create mode 100644 lib/fb/src/main/cpp/jni/Countable.cpp create mode 100644 lib/fb/src/main/cpp/jni/Environment.cpp create mode 100644 lib/fb/src/main/cpp/jni/Exceptions.cpp create mode 100644 lib/fb/src/main/cpp/jni/Hybrid.cpp create mode 100644 lib/fb/src/main/cpp/jni/LocalString.cpp create mode 100644 lib/fb/src/main/cpp/jni/OnLoad.cpp create mode 100644 lib/fb/src/main/cpp/jni/References.cpp create mode 100644 lib/fb/src/main/cpp/jni/WeakReference.cpp create mode 100644 lib/fb/src/main/cpp/jni/fbjni.cpp create mode 100644 lib/fb/src/main/cpp/jni/jni_helpers.cpp create mode 100644 lib/fb/src/main/cpp/log.cpp create mode 100644 lib/fb/src/main/cpp/lyra/lyra.cpp create mode 100644 lib/fb/src/main/cpp/onload.cpp create mode 100644 lib/fb/src/main/java/com/facebook/jni/BUCK create mode 100644 lib/fb/src/main/java/com/facebook/jni/Countable.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/CppException.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/CppSystemErrorException.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/HybridData.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/IteratorHelper.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/MapIteratorHelper.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/NativeRunnable.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/ThreadScopeSupport.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/UnknownCppException.java create mode 100644 lib/fb/src/main/java/com/facebook/jni/fbjni.pro create mode 100644 lib/gtest/BUCK create mode 100644 lib/gtest/googletest/.gitignore create mode 100644 lib/gtest/googletest/.travis.yml create mode 100644 lib/gtest/googletest/CMakeLists.txt create mode 100644 lib/gtest/googletest/README.md create mode 100644 lib/gtest/googletest/appveyor.yml create mode 100644 lib/gtest/googletest/googlemock/CHANGES create mode 100644 lib/gtest/googletest/googlemock/CMakeLists.txt create mode 100644 lib/gtest/googletest/googlemock/CONTRIBUTORS create mode 100644 lib/gtest/googletest/googlemock/LICENSE create mode 100644 lib/gtest/googletest/googlemock/Makefile.am create mode 100644 lib/gtest/googletest/googlemock/README.md create mode 100644 lib/gtest/googletest/googlemock/build-aux/.keep create mode 100644 lib/gtest/googletest/googlemock/configure.ac create mode 100644 lib/gtest/googletest/googlemock/docs/CheatSheet.md create mode 100644 lib/gtest/googletest/googlemock/docs/CookBook.md create mode 100644 lib/gtest/googletest/googlemock/docs/DesignDoc.md create mode 100644 lib/gtest/googletest/googlemock/docs/DevGuide.md create mode 100644 lib/gtest/googletest/googlemock/docs/Documentation.md create mode 100644 lib/gtest/googletest/googlemock/docs/ForDummies.md create mode 100644 lib/gtest/googletest/googlemock/docs/FrequentlyAskedQuestions.md create mode 100644 lib/gtest/googletest/googlemock/docs/KnownIssues.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_5/CheatSheet.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_5/CookBook.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_5/Documentation.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_5/ForDummies.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_5/FrequentlyAskedQuestions.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_6/CheatSheet.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_6/CookBook.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_6/Documentation.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_6/ForDummies.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_6/FrequentlyAskedQuestions.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_7/CheatSheet.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_7/CookBook.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_7/Documentation.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_7/ForDummies.md create mode 100644 lib/gtest/googletest/googlemock/docs/v1_7/FrequentlyAskedQuestions.md create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-actions.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-cardinalities.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-actions.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-actions.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-matchers.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-matchers.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-generated-nice-strict.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-matchers.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-more-actions.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-more-matchers.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock-spec-builders.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/gmock.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/custom/gmock-port.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h create mode 100644 lib/gtest/googletest/googlemock/include/gmock/internal/gmock-port.h create mode 100644 lib/gtest/googletest/googlemock/make/Makefile create mode 100644 lib/gtest/googletest/googlemock/msvc/2005/gmock.sln create mode 100644 lib/gtest/googletest/googlemock/msvc/2005/gmock.vcproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2005/gmock_config.vsprops create mode 100644 lib/gtest/googletest/googlemock/msvc/2005/gmock_main.vcproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2005/gmock_test.vcproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2010/gmock.sln create mode 100644 lib/gtest/googletest/googlemock/msvc/2010/gmock.vcxproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2010/gmock_config.props create mode 100644 lib/gtest/googletest/googlemock/msvc/2010/gmock_main.vcxproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2010/gmock_test.vcxproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2015/gmock.sln create mode 100644 lib/gtest/googletest/googlemock/msvc/2015/gmock.vcxproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2015/gmock_config.props create mode 100644 lib/gtest/googletest/googlemock/msvc/2015/gmock_main.vcxproj create mode 100644 lib/gtest/googletest/googlemock/msvc/2015/gmock_test.vcxproj create mode 100755 lib/gtest/googletest/googlemock/scripts/fuse_gmock_files.py create mode 100644 lib/gtest/googletest/googlemock/scripts/generator/LICENSE create mode 100644 lib/gtest/googletest/googlemock/scripts/generator/README create mode 100644 lib/gtest/googletest/googlemock/scripts/generator/README.cppclean create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/__init__.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/ast.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/gmock_class.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/gmock_class_test.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/keywords.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/tokenize.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/cpp/utils.py create mode 100755 lib/gtest/googletest/googlemock/scripts/generator/gmock_gen.py create mode 100755 lib/gtest/googletest/googlemock/scripts/gmock-config.in create mode 100755 lib/gtest/googletest/googlemock/scripts/gmock_doctor.py create mode 100755 lib/gtest/googletest/googlemock/scripts/upload.py create mode 100755 lib/gtest/googletest/googlemock/scripts/upload_gmock.py create mode 100644 lib/gtest/googletest/googlemock/src/gmock-all.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock-cardinalities.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock-internal-utils.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock-matchers.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock-spec-builders.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock.cc create mode 100644 lib/gtest/googletest/googlemock/src/gmock_main.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-actions_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-cardinalities_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-generated-actions_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-generated-function-mockers_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-generated-internal-utils_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-generated-matchers_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-internal-utils_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-matchers_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-more-actions_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-nice-strict_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-port_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock-spec-builders_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_all_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_ex_test.cc create mode 100755 lib/gtest/googletest/googlemock/test/gmock_leak_test.py create mode 100644 lib/gtest/googletest/googlemock/test/gmock_leak_test_.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_link2_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_link_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_link_test.h create mode 100755 lib/gtest/googletest/googlemock/test/gmock_output_test.py create mode 100644 lib/gtest/googletest/googlemock/test/gmock_output_test_.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_output_test_golden.txt create mode 100644 lib/gtest/googletest/googlemock/test/gmock_stress_test.cc create mode 100644 lib/gtest/googletest/googlemock/test/gmock_test.cc create mode 100755 lib/gtest/googletest/googlemock/test/gmock_test_utils.py create mode 100644 lib/gtest/googletest/googletest/.gitignore create mode 100644 lib/gtest/googletest/googletest/CHANGES create mode 100644 lib/gtest/googletest/googletest/CMakeLists.txt create mode 100644 lib/gtest/googletest/googletest/CONTRIBUTORS create mode 100644 lib/gtest/googletest/googletest/LICENSE create mode 100644 lib/gtest/googletest/googletest/Makefile.am create mode 100644 lib/gtest/googletest/googletest/README.md create mode 100644 lib/gtest/googletest/googletest/build-aux/.keep create mode 100644 lib/gtest/googletest/googletest/cmake/internal_utils.cmake create mode 100644 lib/gtest/googletest/googletest/codegear/gtest.cbproj create mode 100644 lib/gtest/googletest/googletest/codegear/gtest.groupproj create mode 100644 lib/gtest/googletest/googletest/codegear/gtest_all.cc create mode 100644 lib/gtest/googletest/googletest/codegear/gtest_link.cc create mode 100644 lib/gtest/googletest/googletest/codegear/gtest_main.cbproj create mode 100644 lib/gtest/googletest/googletest/codegear/gtest_unittest.cbproj create mode 100644 lib/gtest/googletest/googletest/configure.ac create mode 100644 lib/gtest/googletest/googletest/docs/AdvancedGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/DevGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/Documentation.md create mode 100644 lib/gtest/googletest/googletest/docs/FAQ.md create mode 100644 lib/gtest/googletest/googletest/docs/Primer.md create mode 100644 lib/gtest/googletest/googletest/docs/PumpManual.md create mode 100644 lib/gtest/googletest/googletest/docs/Samples.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_AdvancedGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_Documentation.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_FAQ.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_Primer.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_PumpManual.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_5_XcodeGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_AdvancedGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_Documentation.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_FAQ.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_Primer.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_PumpManual.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_Samples.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_6_XcodeGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_AdvancedGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_Documentation.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_FAQ.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_Primer.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_PumpManual.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_Samples.md create mode 100644 lib/gtest/googletest/googletest/docs/V1_7_XcodeGuide.md create mode 100644 lib/gtest/googletest/googletest/docs/XcodeGuide.md create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-death-test.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-message.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-param-test.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-param-test.h.pump create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-printers.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-spi.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-test-part.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest-typed-test.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest_pred_impl.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/gtest_prod.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/custom/gtest-port.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/custom/gtest-printers.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/custom/gtest.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-filepath.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-internal.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-linked_ptr.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-param-util-generated.h.pump create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-param-util.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-port-arch.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-port.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-string.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-tuple.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-tuple.h.pump create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-type-util.h create mode 100644 lib/gtest/googletest/googletest/include/gtest/internal/gtest-type-util.h.pump create mode 100644 lib/gtest/googletest/googletest/m4/acx_pthread.m4 create mode 100644 lib/gtest/googletest/googletest/m4/gtest.m4 create mode 100644 lib/gtest/googletest/googletest/make/Makefile create mode 100644 lib/gtest/googletest/googletest/msvc/gtest-md.sln create mode 100644 lib/gtest/googletest/googletest/msvc/gtest-md.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest.sln create mode 100644 lib/gtest/googletest/googletest/msvc/gtest.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_main-md.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_main.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_prod_test-md.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_prod_test.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_unittest-md.vcproj create mode 100644 lib/gtest/googletest/googletest/msvc/gtest_unittest.vcproj create mode 100644 lib/gtest/googletest/googletest/samples/prime_tables.h create mode 100644 lib/gtest/googletest/googletest/samples/sample1.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample1.h create mode 100644 lib/gtest/googletest/googletest/samples/sample10_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample1_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample2.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample2.h create mode 100644 lib/gtest/googletest/googletest/samples/sample2_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample3-inl.h create mode 100644 lib/gtest/googletest/googletest/samples/sample3_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample4.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample4.h create mode 100644 lib/gtest/googletest/googletest/samples/sample4_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample5_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample6_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample7_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample8_unittest.cc create mode 100644 lib/gtest/googletest/googletest/samples/sample9_unittest.cc create mode 100644 lib/gtest/googletest/googletest/scripts/common.py create mode 100755 lib/gtest/googletest/googletest/scripts/fuse_gtest_files.py create mode 100755 lib/gtest/googletest/googletest/scripts/gen_gtest_pred_impl.py create mode 100755 lib/gtest/googletest/googletest/scripts/gtest-config.in create mode 100755 lib/gtest/googletest/googletest/scripts/pump.py create mode 100755 lib/gtest/googletest/googletest/scripts/release_docs.py create mode 100644 lib/gtest/googletest/googletest/scripts/test/Makefile create mode 100755 lib/gtest/googletest/googletest/scripts/upload.py create mode 100755 lib/gtest/googletest/googletest/scripts/upload_gtest.py create mode 100644 lib/gtest/googletest/googletest/src/gtest-all.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-death-test.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-filepath.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-internal-inl.h create mode 100644 lib/gtest/googletest/googletest/src/gtest-port.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-printers.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-test-part.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest-typed-test.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest.cc create mode 100644 lib/gtest/googletest/googletest/src/gtest_main.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-death-test_ex_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-death-test_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-filepath_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-linked_ptr_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-listener_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-message_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-options_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-param-test2_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-param-test_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-param-test_test.h create mode 100644 lib/gtest/googletest/googletest/test/gtest-port_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-printers_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-test-part_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-tuple_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-typed-test2_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-typed-test_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest-typed-test_test.h create mode 100644 lib/gtest/googletest/googletest/test/gtest-unittest-api_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_all_test.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_break_on_failure_unittest.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_break_on_failure_unittest_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_catch_exceptions_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_catch_exceptions_test_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_color_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_color_test_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_env_var_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_env_var_test_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_environment_test.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_filter_unittest.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_filter_unittest_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_help_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_help_test_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_list_tests_unittest.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_list_tests_unittest_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_main_unittest.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_no_test_unittest.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_output_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_output_test_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_output_test_golden_lin.txt create mode 100644 lib/gtest/googletest/googletest/test/gtest_pred_impl_unittest.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_premature_exit_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_prod_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_repeat_test.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_shuffle_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_shuffle_test_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_sole_header_test.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_stress_test.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_test_utils.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_throw_on_failure_ex_test.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_throw_on_failure_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_throw_on_failure_test_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_uninitialized_test.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_uninitialized_test_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_unittest.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_xml_outfile1_test_.cc create mode 100644 lib/gtest/googletest/googletest/test/gtest_xml_outfile2_test_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_xml_outfiles_test.py create mode 100755 lib/gtest/googletest/googletest/test/gtest_xml_output_unittest.py create mode 100644 lib/gtest/googletest/googletest/test/gtest_xml_output_unittest_.cc create mode 100755 lib/gtest/googletest/googletest/test/gtest_xml_test_utils.py create mode 100644 lib/gtest/googletest/googletest/test/production.cc create mode 100644 lib/gtest/googletest/googletest/test/production.h create mode 100644 lib/gtest/googletest/googletest/xcode/Config/DebugProject.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Config/FrameworkTarget.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Config/General.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Config/ReleaseProject.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Config/StaticLibraryTarget.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Config/TestTarget.xcconfig create mode 100644 lib/gtest/googletest/googletest/xcode/Resources/Info.plist create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/Info.plist create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/runtests.sh create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/widget.cc create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/widget.h create mode 100644 lib/gtest/googletest/googletest/xcode/Samples/FrameworkSample/widget_test.cc create mode 100644 lib/gtest/googletest/googletest/xcode/Scripts/runtests.sh create mode 100755 lib/gtest/googletest/googletest/xcode/Scripts/versiongenerate.py create mode 100644 lib/gtest/googletest/googletest/xcode/gtest.xcodeproj/project.pbxproj create mode 100755 lib/gtest/googletest/travis.sh create mode 100644 yoga_defs.bzl diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 00000000..74a40a76 --- /dev/null +++ b/.buckconfig @@ -0,0 +1,2 @@ +[cxx] + gtest_dep = //lib/gtest:gtest diff --git a/.gitignore b/.gitignore index 777ff26d..b674b996 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,11 @@ Carthage/Build/iOS/*.dSYM Carthage/Build/iOS/*.bcsymbolmap Carthage/Build/tvOS Carthage/Checkouts + +# Buck +/buck-cache/ +/buck-out/ +/.buckconfig.local +/.buckd +/gentest/test.html +.buckversion \ No newline at end of file diff --git a/BUCK b/BUCK new file mode 100644 index 00000000..ccebfb90 --- /dev/null +++ b/BUCK @@ -0,0 +1,45 @@ +# Copyright (c) 2014-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +load("//:yoga_defs.bzl", "LIBRARY_COMPILER_FLAGS", "BASE_COMPILER_FLAGS", "GTEST_TARGET", "yoga_dep", "cxx_library", "cxx_test") + +GMOCK_OVERRIDE_FLAGS = [ + # gmock does not mark mocked methods as override, ignore the warnings in tests + "-Wno-inconsistent-missing-override", +] + +COMPILER_FLAGS = LIBRARY_COMPILER_FLAGS + [ + "-std=c++1y", +] + +TEST_COMPILER_FLAGS = BASE_COMPILER_FLAGS + GMOCK_OVERRIDE_FLAGS + [ + "-std=c++1y", +] + +cxx_library( + name = "yoga", + srcs = glob(["Sources/yoga/*.cpp"]), + header_namespace = "yoga", + exported_headers = subdir_glob([("Sources/yoga", "*.h")]), + compiler_flags = COMPILER_FLAGS, + soname = "libyogacore.$(ext)", + tests = [":YogaTests"], + visibility = ["PUBLIC"], + deps = [ + yoga_dep("lib/fb:ndklog"), + ], +) + +cxx_test( + name = "YogaTests", + srcs = glob(["Sources/yoga-tests/*.cpp"]), + compiler_flags = TEST_COMPILER_FLAGS, + contacts = ["emilsj@fb.com"], + visibility = ["PUBLIC"], + deps = [ + ":yoga", + GTEST_TARGET, + ], +) diff --git a/Sources/yoga-tests/YGAbsolutePositionTest.cpp b/Sources/yoga-tests/YGAbsolutePositionTest.cpp new file mode 100644 index 00000000..a319cf1d --- /dev/null +++ b/Sources/yoga-tests/YGAbsolutePositionTest.cpp @@ -0,0 +1,1031 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGAbsolutePositionTest.html + +#include +#include + +TEST(YogaTest, absolute_layout_width_height_start_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeStart, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_width_height_end_bottom) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeEnd, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_start_top_end_bottom) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeStart, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeEnd, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_width_height_start_top_end_bottom) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeStart, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeEnd, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, do_not_clamp_height_of_absolute_node_to_height_of_its_overflow_hidden_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetOverflow(root, YGOverflowHidden); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeStart, 0); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 0); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 100); + YGNodeStyleSetHeight(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_within_border) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root, YGEdgeTop, 10); + YGNodeStyleSetMargin(root, YGEdgeRight, 10); + YGNodeStyleSetMargin(root, YGEdgeBottom, 10); + YGNodeStyleSetPadding(root, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root, YGEdgeTop, 10); + YGNodeStyleSetPadding(root, YGEdgeRight, 10); + YGNodeStyleSetPadding(root, YGEdgeBottom, 10); + YGNodeStyleSetBorder(root, YGEdgeLeft, 10); + YGNodeStyleSetBorder(root, YGEdgeTop, 10); + YGNodeStyleSetBorder(root, YGEdgeRight, 10); + YGNodeStyleSetBorder(root, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 0); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 0); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child1, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child1, YGEdgeRight, 0); + YGNodeStyleSetPosition(root_child1, YGEdgeBottom, 0); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child2, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child2, YGEdgeLeft, 0); + YGNodeStyleSetPosition(root_child2, YGEdgeTop, 0); + YGNodeStyleSetMargin(root_child2, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child2, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child2, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child2, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child3, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child3, YGEdgeRight, 0); + YGNodeStyleSetPosition(root_child3, YGEdgeBottom, 0); + YGNodeStyleSetMargin(root_child3, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetAlignItems(root, YGAlignFlexEnd); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_justify_content_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_center_on_child_only) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignCenter); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_center_and_top_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_center_and_bottom_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_center_and_left_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 5); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_align_items_and_justify_content_center_and_right_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 110); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeRight, 5); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, position_root_with_rtl_should_position_withoutdirection) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root, YGEdgeLeft, 72); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_percentage_bottom_based_on_parent_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeTop, 50); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child1, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child1, YGEdgeBottom, 50); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child2, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child2, YGEdgeTop, 10); + YGNodeStyleSetPositionPercent(root_child2, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_in_wrap_reverse_column_container) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_in_wrap_reverse_row_container) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_in_wrap_reverse_column_container_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexEnd); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, absolute_layout_in_wrap_reverse_row_container_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexEnd); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGAlignContentTest.cpp b/Sources/yoga-tests/YGAlignContentTest.cpp new file mode 100644 index 00000000..7cd5c095 --- /dev/null +++ b/Sources/yoga-tests/YGAlignContentTest.cpp @@ -0,0 +1,1878 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGAlignContentTest.html + +#include +#include + +TEST(YogaTest, align_content_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 130); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 10); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeStyleSetHeight(root_child4, 10); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(130, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(130, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_flex_start_without_height_on_children) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 10); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_flex_start_with_flex) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 120); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 0); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child3, 1); + YGNodeStyleSetFlexShrink(root_child3, 1); + YGNodeStyleSetFlexBasisPercent(root_child3, 0); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root, YGAlignFlexEnd); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 10); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeStyleSetHeight(root_child4, 10); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_spacebetween) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignSpaceBetween); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 130); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 10); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeStyleSetHeight(root_child4, 10); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(130, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(130, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_spacearound) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignSpaceAround); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 140); + YGNodeStyleSetHeight(root, 120); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 10); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeStyleSetHeight(root_child4, 10); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(95, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(95, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_children) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0_child0, 0); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_flex) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child3, 1); + YGNodeStyleSetFlexShrink(root_child3, 1); + YGNodeStyleSetFlexBasisPercent(root_child3, 0); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_flex_no_shrink) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child3, 1); + YGNodeStyleSetFlexBasisPercent(root_child3, 0); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child1, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child1, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child1, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child1, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child3, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child3, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root_child1, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root_child1, YGEdgeTop, 10); + YGNodeStyleSetPadding(root_child1, YGEdgeRight, 10); + YGNodeStyleSetPadding(root_child1, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root_child3, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root_child3, YGEdgeTop, 10); + YGNodeStyleSetPadding(root_child3, YGEdgeRight, 10); + YGNodeStyleSetPadding(root_child3, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_single_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_fixed_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 60); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetMaxHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_row_with_min_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetMinHeight(root_child1, 80); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 150); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0_child0, 0); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_content_stretch_is_not_overriding_align_items) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root_child0, YGAlignStretch); + YGNodeStyleSetAlignItems(root_child0, YGAlignCenter); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root_child0_child0, YGAlignStretch); + YGNodeStyleSetWidth(root_child0_child0, 10); + YGNodeStyleSetHeight(root_child0_child0, 10); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGAlignItemsTest.cpp b/Sources/yoga-tests/YGAlignItemsTest.cpp new file mode 100644 index 00000000..fd5e424f --- /dev/null +++ b/Sources/yoga-tests/YGAlignItemsTest.cpp @@ -0,0 +1,2158 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGAlignItemsTest.html + +#include +#include + +TEST(YogaTest, align_items_stretch) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexEnd); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_multiline) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child1, YGWrapWrap); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 25); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child1, 25); + YGNodeStyleSetHeight(root_child1_child1, 10); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + const YGNodeRef root_child1_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child2, 25); + YGNodeStyleSetHeight(root_child1_child2, 20); + YGNodeInsertChild(root_child1, root_child1_child2, 2); + + const YGNodeRef root_child1_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child3, 25); + YGNodeStyleSetHeight(root_child1_child3, 10); + YGNodeInsertChild(root_child1, root_child1_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_multiline_override) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child1, YGWrapWrap); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 25); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child1_child1, YGAlignBaseline); + YGNodeStyleSetWidth(root_child1_child1, 25); + YGNodeStyleSetHeight(root_child1_child1, 10); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + const YGNodeRef root_child1_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child2, 25); + YGNodeStyleSetHeight(root_child1_child2, 20); + YGNodeInsertChild(root_child1, root_child1_child2, 2); + + const YGNodeRef root_child1_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child1_child3, YGAlignBaseline); + YGNodeStyleSetWidth(root_child1_child3, 25); + YGNodeStyleSetHeight(root_child1_child3, 10); + YGNodeInsertChild(root_child1, root_child1_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_multiline_no_override_on_secondline) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child1, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child1, YGWrapWrap); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 25); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child1, 25); + YGNodeStyleSetHeight(root_child1_child1, 10); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + const YGNodeRef root_child1_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child2, 25); + YGNodeStyleSetHeight(root_child1_child2, 20); + YGNodeInsertChild(root_child1, root_child1_child2, 2); + + const YGNodeRef root_child1_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child1_child3, YGAlignBaseline); + YGNodeStyleSetWidth(root_child1_child3, 25); + YGNodeStyleSetHeight(root_child1_child3, 10); + YGNodeInsertChild(root_child1, root_child1_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child1_child3)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_top2) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root_child1, YGEdgeTop, 5); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_double_nested_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 50); + YGNodeStyleSetHeight(root_child0_child0, 20); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 15); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 5); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child1_child0, YGEdgeLeft, 1); + YGNodeStyleSetMargin(root_child1_child0, YGEdgeTop, 1); + YGNodeStyleSetMargin(root_child1_child0, YGEdgeRight, 1); + YGNodeStyleSetMargin(root_child1_child0, YGEdgeBottom, 1); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(44, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(1, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(1, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(44, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(-1, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(1, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_child_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetPadding(root, YGEdgeLeft, 5); + YGNodeStyleSetPadding(root, YGEdgeTop, 5); + YGNodeStyleSetPadding(root, YGEdgeRight, 5); + YGNodeStyleSetPadding(root, YGEdgeBottom, 5); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root_child1, YGEdgeLeft, 5); + YGNodeStyleSetPadding(root_child1, YGEdgeTop, 5); + YGNodeStyleSetPadding(root_child1, YGEdgeRight, 5); + YGNodeStyleSetPadding(root_child1, YGEdgeBottom, 5); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(-5, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(-5, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_multiline) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 20); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child2_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2_child0, 50); + YGNodeStyleSetHeight(root_child2_child0, 10); + YGNodeInsertChild(root_child2, root_child2_child0, 0); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 50); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_multiline_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 20); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 40); + YGNodeStyleSetHeight(root_child2, 70); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child2_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2_child0, 10); + YGNodeStyleSetHeight(root_child2_child0, 10); + YGNodeInsertChild(root_child2, root_child2_child0, 0); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 20); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_multiline_column2) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 20); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 40); + YGNodeStyleSetHeight(root_child2, 70); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child2_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2_child0, 10); + YGNodeStyleSetHeight(root_child2_child0, 10); + YGNodeInsertChild(root_child2, root_child2_child0, 0); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 20); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_baseline_multiline_row_and_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 20); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child2_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2_child0, 50); + YGNodeStyleSetHeight(root_child2_child0, 10); + YGNodeInsertChild(root_child2, root_child2_child0, 0); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 50); + YGNodeStyleSetHeight(root_child3, 20); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_center_child_with_margin_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignCenter); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeRight, 10); + YGNodeStyleSetWidth(root_child0_child0, 52); + YGNodeStyleSetHeight(root_child0_child0, 52); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_flex_end_child_with_margin_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexEnd); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeRight, 10); + YGNodeStyleSetWidth(root_child0_child0, 52); + YGNodeStyleSetHeight(root_child0_child0, 52); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_center_child_without_margin_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignCenter); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 72); + YGNodeStyleSetHeight(root_child0_child0, 72); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_flex_end_child_without_margin_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexEnd); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 72); + YGNodeStyleSetHeight(root_child0_child0, 72); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_center_should_size_based_on_content) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root_child0, YGJustifyCenter); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 20); + YGNodeStyleSetHeight(root_child0_child0_child0, 20); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_strech_should_size_based_on_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root_child0, YGJustifyCenter); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 20); + YGNodeStyleSetHeight(root_child0_child0_child0, 20); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_flex_start_with_shrinking_children) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexStart); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0_child0, 1); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_flex_start_with_stretching_children) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0_child0, 1); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_flex_start_with_shrinking_children_with_stretch) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexStart); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0_child0, 1); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGAlignSelfTest.cpp b/Sources/yoga-tests/YGAlignSelfTest.cpp new file mode 100644 index 00000000..00bb7b9a --- /dev/null +++ b/Sources/yoga-tests/YGAlignSelfTest.cpp @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGAlignSelfTest.html + +#include +#include + +TEST(YogaTest, align_self_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignCenter); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_self_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexEnd); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_self_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexStart); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_self_flex_end_override_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexEnd); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_self_baseline) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child0, YGAlignBaseline); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignSelf(root_child1, YGAlignBaseline); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1_child0, 50); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGAndroidNewsFeed.cpp b/Sources/yoga-tests/YGAndroidNewsFeed.cpp new file mode 100644 index 00000000..fb3f0730 --- /dev/null +++ b/Sources/yoga-tests/YGAndroidNewsFeed.cpp @@ -0,0 +1,445 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGAndroidNewsFeed.html + +#include +#include + +TEST(YogaTest, android_news_feed) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetWidth(root, 1080); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root_child0_child0, YGAlignStretch); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root_child0_child0_child0, YGAlignStretch); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child0_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root_child0_child0_child0_child0, YGAlignStretch); + YGNodeStyleSetAlignItems(root_child0_child0_child0_child0, YGAlignFlexStart); + YGNodeStyleSetMargin(root_child0_child0_child0_child0, YGEdgeStart, 36); + YGNodeStyleSetMargin(root_child0_child0_child0_child0, YGEdgeTop, 24); + YGNodeInsertChild( + root_child0_child0_child0, root_child0_child0_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0_child0_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child0_child0_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent( + root_child0_child0_child0_child0_child0, YGAlignStretch); + YGNodeInsertChild( + root_child0_child0_child0_child0, + root_child0_child0_child0_child0_child0, + 0); + + const YGNodeRef root_child0_child0_child0_child0_child0_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child0_child0_child0_child0, YGAlignStretch); + YGNodeStyleSetWidth(root_child0_child0_child0_child0_child0_child0, 120); + YGNodeStyleSetHeight(root_child0_child0_child0_child0_child0_child0, 120); + YGNodeInsertChild( + root_child0_child0_child0_child0_child0, + root_child0_child0_child0_child0_child0_child0, + 0); + + const YGNodeRef root_child0_child0_child0_child0_child1 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child0_child0_child1, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child0_child0_child1, 1); + YGNodeStyleSetMargin( + root_child0_child0_child0_child0_child1, YGEdgeRight, 36); + YGNodeStyleSetPadding( + root_child0_child0_child0_child0_child1, YGEdgeLeft, 36); + YGNodeStyleSetPadding(root_child0_child0_child0_child0_child1, YGEdgeTop, 21); + YGNodeStyleSetPadding( + root_child0_child0_child0_child0_child1, YGEdgeRight, 36); + YGNodeStyleSetPadding( + root_child0_child0_child0_child0_child1, YGEdgeBottom, 18); + YGNodeInsertChild( + root_child0_child0_child0_child0, + root_child0_child0_child0_child0_child1, + 1); + + const YGNodeRef root_child0_child0_child0_child0_child1_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child0_child0_child1_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent( + root_child0_child0_child0_child0_child1_child0, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child0_child0_child1_child0, 1); + YGNodeInsertChild( + root_child0_child0_child0_child0_child1, + root_child0_child0_child0_child0_child1_child0, + 0); + + const YGNodeRef root_child0_child0_child0_child0_child1_child1 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child0_child0_child1_child1, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child0_child0_child1_child1, 1); + YGNodeInsertChild( + root_child0_child0_child0_child0_child1, + root_child0_child0_child0_child0_child1_child1, + 1); + + const YGNodeRef root_child0_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent(root_child0_child0_child1, YGAlignStretch); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child1, 1); + + const YGNodeRef root_child0_child0_child1_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child1_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root_child0_child0_child1_child0, YGAlignStretch); + YGNodeStyleSetAlignItems(root_child0_child0_child1_child0, YGAlignFlexStart); + YGNodeStyleSetMargin(root_child0_child0_child1_child0, YGEdgeStart, 174); + YGNodeStyleSetMargin(root_child0_child0_child1_child0, YGEdgeTop, 24); + YGNodeInsertChild( + root_child0_child0_child1, root_child0_child0_child1_child0, 0); + + const YGNodeRef root_child0_child0_child1_child0_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child1_child0_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent( + root_child0_child0_child1_child0_child0, YGAlignStretch); + YGNodeInsertChild( + root_child0_child0_child1_child0, + root_child0_child0_child1_child0_child0, + 0); + + const YGNodeRef root_child0_child0_child1_child0_child0_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child1_child0_child0_child0, YGAlignStretch); + YGNodeStyleSetWidth(root_child0_child0_child1_child0_child0_child0, 72); + YGNodeStyleSetHeight(root_child0_child0_child1_child0_child0_child0, 72); + YGNodeInsertChild( + root_child0_child0_child1_child0_child0, + root_child0_child0_child1_child0_child0_child0, + 0); + + const YGNodeRef root_child0_child0_child1_child0_child1 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child1_child0_child1, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child1_child0_child1, 1); + YGNodeStyleSetMargin( + root_child0_child0_child1_child0_child1, YGEdgeRight, 36); + YGNodeStyleSetPadding( + root_child0_child0_child1_child0_child1, YGEdgeLeft, 36); + YGNodeStyleSetPadding(root_child0_child0_child1_child0_child1, YGEdgeTop, 21); + YGNodeStyleSetPadding( + root_child0_child0_child1_child0_child1, YGEdgeRight, 36); + YGNodeStyleSetPadding( + root_child0_child0_child1_child0_child1, YGEdgeBottom, 18); + YGNodeInsertChild( + root_child0_child0_child1_child0, + root_child0_child0_child1_child0_child1, + 1); + + const YGNodeRef root_child0_child0_child1_child0_child1_child0 = + YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection( + root_child0_child0_child1_child0_child1_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignContent( + root_child0_child0_child1_child0_child1_child0, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child1_child0_child1_child0, 1); + YGNodeInsertChild( + root_child0_child0_child1_child0_child1, + root_child0_child0_child1_child0_child1_child0, + 0); + + const YGNodeRef root_child0_child0_child1_child0_child1_child1 = + YGNodeNewWithConfig(config); + YGNodeStyleSetAlignContent( + root_child0_child0_child1_child0_child1_child1, YGAlignStretch); + YGNodeStyleSetFlexShrink(root_child0_child0_child1_child0_child1_child1, 1); + YGNodeInsertChild( + root_child0_child0_child1_child0_child1, + root_child0_child0_child1_child0_child1_child1, + 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(144, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetLeft(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetTop(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(1044, YGNodeLayoutGetWidth(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, + YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, + YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 120, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 39, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(144, YGNodeLayoutGetTop(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(96, YGNodeLayoutGetHeight(root_child0_child0_child1)); + + ASSERT_FLOAT_EQ(174, YGNodeLayoutGetLeft(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetTop(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(906, YGNodeLayoutGetWidth(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 72, + YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 39, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(144, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetTop(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(1044, YGNodeLayoutGetWidth(root_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 924, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, + YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 120, + YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 816, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1)); + ASSERT_FLOAT_EQ( + 39, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child0_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child0_child0_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(144, YGNodeLayoutGetTop(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(1080, YGNodeLayoutGetWidth(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(96, YGNodeLayoutGetHeight(root_child0_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetTop(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(906, YGNodeLayoutGetWidth(root_child0_child0_child1_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 834, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child0)); + + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child0_child0)); + ASSERT_FLOAT_EQ( + 72, + YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child0_child0)); + + ASSERT_FLOAT_EQ( + 726, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 72, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1)); + ASSERT_FLOAT_EQ( + 39, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1_child0)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1_child0)); + + ASSERT_FLOAT_EQ( + 36, YGNodeLayoutGetLeft(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 21, YGNodeLayoutGetTop(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetWidth(root_child0_child0_child1_child0_child1_child1)); + ASSERT_FLOAT_EQ( + 0, YGNodeLayoutGetHeight(root_child0_child0_child1_child0_child1_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGAspectRatioTest.cpp b/Sources/yoga-tests/YGAspectRatioTest.cpp new file mode 100644 index 00000000..ec84aa92 --- /dev/null +++ b/Sources/yoga-tests/YGAspectRatioTest.cpp @@ -0,0 +1,898 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +static YGSize _measure(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + return YGSize{ + .width = widthMode == YGMeasureModeExactly ? width : 50, + .height = heightMode == YGMeasureModeExactly ? height : 50, + }; +} + +TEST(YogaTest, aspect_ratio_cross_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_main_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_both_dimensions_defined_row) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_both_dimensions_defined_column) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_align_stretch) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_flex_grow) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_flex_shrink) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 150); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_flex_shrink_2) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeightPercent(root_child0, 100); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetHeightPercent(root_child1, 100); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetAspectRatio(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_basis) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_absolute_layout_width_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 0); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 0); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_absolute_layout_height_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 0); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 0); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_with_max_cross_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetMaxWidth(root_child0, 40); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_with_max_main_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetMaxHeight(root_child0, 40); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_with_min_cross_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 30); + YGNodeStyleSetMinWidth(root_child0, 40); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(30, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_with_min_main_defined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetMinHeight(root_child0, 40); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_double_cross) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 2); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_half_cross) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeStyleSetAspectRatio(root_child0, 0.5); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_double_main) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 0.5); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_half_main) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetAspectRatio(root_child0, 2); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_with_measure_func) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_width_height_flex_grow_row) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_width_height_flex_grow_column) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_height_as_flex_basis) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetHeight(root_child1, 100); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetAspectRatio(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(75, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_EQ(125, YGNodeLayoutGetWidth(root_child1)); + ASSERT_EQ(125, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_width_as_flex_basis) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 100); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetAspectRatio(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(75, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_EQ(125, YGNodeLayoutGetWidth(root_child1)); + ASSERT_EQ(125, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_overrides_flex_grow_row) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 0.5); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_overrides_flex_grow_column) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetAspectRatio(root_child0, 2); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_left_right_absolute) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeRight, 10); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_top_bottom_absolute) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeTop, 10); + YGNodeStyleSetPosition(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_width_overrides_align_stretch_row) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_height_overrides_align_stretch_column) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_allow_child_overflow_parent_size) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 4); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_defined_main_with_margin) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_defined_cross_with_margin) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_defined_cross_with_main_margin) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetAspectRatio(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_should_prefer_explicit_height) { + const YGConfigRef config = YGConfigNew(); + YGConfigSetUseWebDefaults(config, true); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionColumn); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0_child0, YGFlexDirectionColumn); + YGNodeStyleSetHeight(root_child0_child0, 100); + YGNodeStyleSetAspectRatio(root_child0_child0, 2); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + YGNodeCalculateLayout(root, 100, 200, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_should_prefer_explicit_width) { + const YGConfigRef config = YGConfigNew(); + YGConfigSetUseWebDefaults(config, true); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0_child0, YGFlexDirectionRow); + YGNodeStyleSetWidth(root_child0_child0, 100); + YGNodeStyleSetAspectRatio(root_child0_child0, 0.5); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, aspect_ratio_should_prefer_flexed_dimension) { + const YGConfigRef config = YGConfigNew(); + YGConfigSetUseWebDefaults(config, true); + + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionColumn); + YGNodeStyleSetAspectRatio(root_child0, 2); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAspectRatio(root_child0_child0, 4); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGBaselineFuncTest.cpp b/Sources/yoga-tests/YGBaselineFuncTest.cpp new file mode 100644 index 00000000..c8d1ff1a --- /dev/null +++ b/Sources/yoga-tests/YGBaselineFuncTest.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +static float _baseline(YGNodeRef node, const float width, const float height) { + float* baseline = (float*)node->getContext(); + return *baseline; +} + +TEST(YogaTest, align_baseline_customer_func) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignBaseline); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + float baselineValue = 10; + const YGNodeRef root_child1_child0 = YGNodeNew(); + root_child1_child0->setContext(&baselineValue); + YGNodeStyleSetWidth(root_child1_child0, 50); + root_child1_child0->setBaseLineFunc(_baseline); + YGNodeStyleSetHeight(root_child1_child0, 20); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1_child0)); +} diff --git a/Sources/yoga-tests/YGBorderTest.cpp b/Sources/yoga-tests/YGBorderTest.cpp new file mode 100644 index 00000000..d4e63764 --- /dev/null +++ b/Sources/yoga-tests/YGBorderTest.cpp @@ -0,0 +1,212 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGBorderTest.html + +#include +#include + +TEST(YogaTest, border_no_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetBorder(root, YGEdgeLeft, 10); + YGNodeStyleSetBorder(root, YGEdgeTop, 10); + YGNodeStyleSetBorder(root, YGEdgeRight, 10); + YGNodeStyleSetBorder(root, YGEdgeBottom, 10); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, border_container_match_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetBorder(root, YGEdgeLeft, 10); + YGNodeStyleSetBorder(root, YGEdgeTop, 10); + YGNodeStyleSetBorder(root, YGEdgeRight, 10); + YGNodeStyleSetBorder(root, YGEdgeBottom, 10); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, border_flex_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetBorder(root, YGEdgeLeft, 10); + YGNodeStyleSetBorder(root, YGEdgeTop, 10); + YGNodeStyleSetBorder(root, YGEdgeRight, 10); + YGNodeStyleSetBorder(root, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, border_stretch_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetBorder(root, YGEdgeLeft, 10); + YGNodeStyleSetBorder(root, YGEdgeTop, 10); + YGNodeStyleSetBorder(root, YGEdgeRight, 10); + YGNodeStyleSetBorder(root, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, border_center_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetBorder(root, YGEdgeStart, 10); + YGNodeStyleSetBorder(root, YGEdgeEnd, 20); + YGNodeStyleSetBorder(root, YGEdgeBottom, 20); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGComputedMarginTest.cpp b/Sources/yoga-tests/YGComputedMarginTest.cpp new file mode 100644 index 00000000..9cbbe4e0 --- /dev/null +++ b/Sources/yoga-tests/YGComputedMarginTest.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, computed_layout_margin) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetMarginPercent(root, YGEdgeStart, 10); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetMargin(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeRight)); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetMargin(root, YGEdgeRight)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGComputedPaddingTest.cpp b/Sources/yoga-tests/YGComputedPaddingTest.cpp new file mode 100644 index 00000000..bf31fe7b --- /dev/null +++ b/Sources/yoga-tests/YGComputedPaddingTest.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, computed_layout_padding) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetPaddingPercent(root, YGEdgeStart, 10); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeRight)); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(root, YGEdgeRight)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGDefaultValuesTest.cpp b/Sources/yoga-tests/YGDefaultValuesTest.cpp new file mode 100644 index 00000000..ca143866 --- /dev/null +++ b/Sources/yoga-tests/YGDefaultValuesTest.cpp @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, assert_default_values) { + const YGNodeRef root = YGNodeNew(); + + ASSERT_EQ(0, YGNodeGetChildCount(root)); + ASSERT_EQ(NULL, YGNodeGetChild(root, 1)); + + ASSERT_EQ(YGDirectionInherit, YGNodeStyleGetDirection(root)); + ASSERT_EQ(YGFlexDirectionColumn, YGNodeStyleGetFlexDirection(root)); + ASSERT_EQ(YGJustifyFlexStart, YGNodeStyleGetJustifyContent(root)); + ASSERT_EQ(YGAlignFlexStart, YGNodeStyleGetAlignContent(root)); + ASSERT_EQ(YGAlignStretch, YGNodeStyleGetAlignItems(root)); + ASSERT_EQ(YGAlignAuto, YGNodeStyleGetAlignSelf(root)); + ASSERT_EQ(YGPositionTypeRelative, YGNodeStyleGetPositionType(root)); + ASSERT_EQ(YGWrapNoWrap, YGNodeStyleGetFlexWrap(root)); + ASSERT_EQ(YGOverflowVisible, YGNodeStyleGetOverflow(root)); + ASSERT_FLOAT_EQ(0, YGNodeStyleGetFlexGrow(root)); + ASSERT_FLOAT_EQ(0, YGNodeStyleGetFlexShrink(root)); + ASSERT_EQ(YGNodeStyleGetFlexBasis(root).unit, YGUnitAuto); + + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeLeft).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeTop).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeRight).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeBottom).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeStart).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPosition(root, YGEdgeEnd).unit, YGUnitUndefined); + + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeLeft).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeTop).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeRight).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeBottom).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeStart).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMargin(root, YGEdgeEnd).unit, YGUnitUndefined); + + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeLeft).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeTop).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeRight).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeBottom).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeStart).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetPadding(root, YGEdgeEnd).unit, YGUnitUndefined); + + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeLeft))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeTop))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeRight))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeBottom))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeStart))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeStyleGetBorder(root, YGEdgeEnd))); + + ASSERT_EQ(YGNodeStyleGetWidth(root).unit, YGUnitAuto); + ASSERT_EQ(YGNodeStyleGetHeight(root).unit, YGUnitAuto); + ASSERT_EQ(YGNodeStyleGetMinWidth(root).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMinHeight(root).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMaxWidth(root).unit, YGUnitUndefined); + ASSERT_EQ(YGNodeStyleGetMaxHeight(root).unit, YGUnitUndefined); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetRight(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetBottom(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeTop)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeRight)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(root, YGEdgeBottom)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeTop)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeRight)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeBottom)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetBorder(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetBorder(root, YGEdgeTop)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetBorder(root, YGEdgeRight)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetBorder(root, YGEdgeBottom)); + + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetWidth(root))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetHeight(root))); + ASSERT_EQ(YGDirectionInherit, YGNodeLayoutGetDirection(root)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, assert_webdefault_values) { + YGConfig *config = YGConfigNew(); + YGConfigSetUseWebDefaults(config, true); + const YGNodeRef root = YGNodeNewWithConfig(config); + + ASSERT_EQ(YGFlexDirectionRow, YGNodeStyleGetFlexDirection(root)); + ASSERT_EQ(YGAlignStretch, YGNodeStyleGetAlignContent(root)); + ASSERT_FLOAT_EQ(1.0f, YGNodeStyleGetFlexShrink(root)); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(YogaTest, assert_webdefault_values_reset) { + YGConfig *config = YGConfigNew(); + YGConfigSetUseWebDefaults(config, true); + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeReset(root); + + ASSERT_EQ(YGFlexDirectionRow, YGNodeStyleGetFlexDirection(root)); + ASSERT_EQ(YGAlignStretch, YGNodeStyleGetAlignContent(root)); + ASSERT_FLOAT_EQ(1.0f, YGNodeStyleGetFlexShrink(root)); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(YogaTest, assert_legacy_stretch_behaviour) { + YGConfig *config = YGConfigNew(); + YGConfigSetUseLegacyStretchBehaviour(config, true); + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexStart); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0_child0, 1); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGDimensionTest.cpp b/Sources/yoga-tests/YGDimensionTest.cpp new file mode 100644 index 00000000..42fc123a --- /dev/null +++ b/Sources/yoga-tests/YGDimensionTest.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGDimensionTest.html + +#include +#include + +TEST(YogaTest, wrap_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_grandchild) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 100); + YGNodeStyleSetHeight(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGDirtiedTest.cpp b/Sources/yoga-tests/YGDirtiedTest.cpp new file mode 100644 index 00000000..bb0c05d0 --- /dev/null +++ b/Sources/yoga-tests/YGDirtiedTest.cpp @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +static void _dirtied(YGNodeRef node) { + int* dirtiedCount = (int*)node->getContext(); + (*dirtiedCount)++; +} + +TEST(YogaTest, dirtied) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + int dirtiedCount = 0; + root->setContext(&dirtiedCount); + root->setDirtiedFunc(_dirtied); + + ASSERT_EQ(0, dirtiedCount); + + // `_dirtied` MUST be called in case of explicit dirtying. + root->setDirty(true); + ASSERT_EQ(1, dirtiedCount); + + // `_dirtied` MUST be called ONCE. + root->setDirty(true); + ASSERT_EQ(1, dirtiedCount); +} + +TEST(YogaTest, dirtied_propagation) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + int dirtiedCount = 0; + root->setContext(&dirtiedCount); + root->setDirtiedFunc(_dirtied); + + ASSERT_EQ(0, dirtiedCount); + + // `_dirtied` MUST be called for the first time. + root_child0->markDirtyAndPropogate(); + ASSERT_EQ(1, dirtiedCount); + + // `_dirtied` must NOT be called for the second time. + root_child0->markDirtyAndPropogate(); + ASSERT_EQ(1, dirtiedCount); +} + +TEST(YogaTest, dirtied_hierarchy) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + int dirtiedCount = 0; + root_child0->setContext(&dirtiedCount); + root_child0->setDirtiedFunc(_dirtied); + + ASSERT_EQ(0, dirtiedCount); + + // `_dirtied` must NOT be called for descendants. + root->markDirtyAndPropogate(); + ASSERT_EQ(0, dirtiedCount); + + // `_dirtied` must NOT be called for the sibling node. + root_child1->markDirtyAndPropogate(); + ASSERT_EQ(0, dirtiedCount); + + // `_dirtied` MUST be called in case of explicit dirtying. + root_child0->markDirtyAndPropogate(); + ASSERT_EQ(1, dirtiedCount); +} diff --git a/Sources/yoga-tests/YGDirtyMarkingTest.cpp b/Sources/yoga-tests/YGDirtyMarkingTest.cpp new file mode 100644 index 00000000..d951f53e --- /dev/null +++ b/Sources/yoga-tests/YGDirtyMarkingTest.cpp @@ -0,0 +1,159 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, dirty_propagation) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeStyleSetWidth(root_child0, 20); + + EXPECT_TRUE(root_child0->isDirty()); + EXPECT_FALSE(root_child1->isDirty()); + EXPECT_TRUE(root->isDirty()); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FALSE(root_child0->isDirty()); + EXPECT_FALSE(root_child1->isDirty()); + EXPECT_FALSE(root->isDirty()); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dirty_propagation_only_if_prop_changed) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNew(); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeStyleSetWidth(root_child0, 50); + + EXPECT_FALSE(root_child0->isDirty()); + EXPECT_FALSE(root_child1->isDirty()); + EXPECT_FALSE(root->isDirty()); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dirty_mark_all_children_as_dirty_when_display_changes) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(child0, 1); + const YGNodeRef child1 = YGNodeNew(); + YGNodeStyleSetFlexGrow(child1, 1); + + const YGNodeRef child1_child0 = YGNodeNew(); + const YGNodeRef child1_child0_child0 = YGNodeNew(); + YGNodeStyleSetWidth(child1_child0_child0, 8); + YGNodeStyleSetHeight(child1_child0_child0, 16); + + YGNodeInsertChild(child1_child0, child1_child0_child0, 0); + + YGNodeInsertChild(child1, child1_child0, 0); + YGNodeInsertChild(root, child0, 0); + YGNodeInsertChild(root, child1, 0); + + YGNodeStyleSetDisplay(child0, YGDisplayFlex); + YGNodeStyleSetDisplay(child1, YGDisplayNone); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(child1_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(child1_child0_child0)); + + YGNodeStyleSetDisplay(child0, YGDisplayNone); + YGNodeStyleSetDisplay(child1, YGDisplayFlex); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(8, YGNodeLayoutGetWidth(child1_child0_child0)); + ASSERT_FLOAT_EQ(16, YGNodeLayoutGetHeight(child1_child0_child0)); + + YGNodeStyleSetDisplay(child0, YGDisplayFlex); + YGNodeStyleSetDisplay(child1, YGDisplayNone); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(child1_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(child1_child0_child0)); + + YGNodeStyleSetDisplay(child0, YGDisplayNone); + YGNodeStyleSetDisplay(child1, YGDisplayFlex); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(8, YGNodeLayoutGetWidth(child1_child0_child0)); + ASSERT_FLOAT_EQ(16, YGNodeLayoutGetHeight(child1_child0_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dirty_node_only_if_children_are_actually_removed) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef child0 = YGNodeNew(); + YGNodeStyleSetWidth(child0, 50); + YGNodeStyleSetHeight(child0, 25); + YGNodeInsertChild(root, child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + const YGNodeRef child1 = YGNodeNew(); + YGNodeRemoveChild(root, child1); + EXPECT_FALSE(root->isDirty()); + YGNodeFree(child1); + + YGNodeRemoveChild(root, child0); + EXPECT_TRUE(root->isDirty()); + YGNodeFree(child0); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dirty_node_only_if_undefined_values_gets_set_to_undefined) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + YGNodeStyleSetMinWidth(root, YGUndefined); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FALSE(root->isDirty()); + + YGNodeStyleSetMinWidth(root, YGUndefined); + + EXPECT_FALSE(root->isDirty()); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGDisplayTest.cpp b/Sources/yoga-tests/YGDisplayTest.cpp new file mode 100644 index 00000000..45256ac9 --- /dev/null +++ b/Sources/yoga-tests/YGDisplayTest.cpp @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGDisplayTest.html + +#include +#include + +TEST(YogaTest, display_none) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetDisplay(root_child1, YGDisplayNone); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, display_none_fixed_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 20); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeStyleSetDisplay(root_child1, YGDisplayNone); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, display_none_with_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeStyleSetDisplay(root_child0, YGDisplayNone); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, display_none_with_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 0); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetDisplay(root_child1, YGDisplayNone); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child0, 1); + YGNodeStyleSetFlexShrink(root_child1_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child1_child0, 0); + YGNodeStyleSetWidth(root_child1_child0, 20); + YGNodeStyleSetMinWidth(root_child1_child0, 0); + YGNodeStyleSetMinHeight(root_child1_child0, 0); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetFlexShrink(root_child2, 1); + YGNodeStyleSetFlexBasisPercent(root_child2, 0); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, display_none_with_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetPosition(root_child1, YGEdgeTop, 10); + YGNodeStyleSetDisplay(root_child1, YGDisplayNone); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGEdgeTest.cpp b/Sources/yoga-tests/YGEdgeTest.cpp new file mode 100644 index 00000000..02de1463 --- /dev/null +++ b/Sources/yoga-tests/YGEdgeTest.cpp @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, start_overrides) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 20); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetRight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetRight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, end_overrides) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeEnd, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 20); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetRight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetRight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, horizontal_overridden) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeHorizontal, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetRight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, vertical_overridden) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeVertical, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetBottom(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, horizontal_overrides_all) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeHorizontal, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeAll, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetRight(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetBottom(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, vertical_overrides_all) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeVertical, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeAll, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetRight(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetBottom(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, all_overridden) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeAll, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetRight(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetBottom(root_child0)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGFlexDirectionTest.cpp b/Sources/yoga-tests/YGFlexDirectionTest.cpp new file mode 100644 index 00000000..93e09425 --- /dev/null +++ b/Sources/yoga-tests/YGFlexDirectionTest.cpp @@ -0,0 +1,415 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGFlexDirectionTest.html + +#include +#include + +TEST(YogaTest, flex_direction_column_no_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_direction_row_no_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_direction_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_direction_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_direction_column_reverse) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumnReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_direction_row_reverse) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRowReverse); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGFlexTest.cpp b/Sources/yoga-tests/YGFlexTest.cpp new file mode 100644 index 00000000..e1a8ba89 --- /dev/null +++ b/Sources/yoga-tests/YGFlexTest.cpp @@ -0,0 +1,491 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGFlexTest.html + +#include +#include + +TEST(YogaTest, flex_basis_flex_grow_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_basis_flex_grow_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_basis_flex_shrink_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_basis_flex_shrink_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_shrink_to_zero) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root, 75); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_basis_overrides_main_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_shrink_at_most) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_less_than_factor_one) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 0.2f); + YGNodeStyleSetFlexBasis(root_child0, 40); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 0.2f); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 0.4f); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(132, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(132, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(224, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(184, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(132, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(132, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(224, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(184, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGFlexWrapTest.cpp b/Sources/yoga-tests/YGFlexWrapTest.cpp new file mode 100644 index 00000000..e8e5458c --- /dev/null +++ b/Sources/yoga-tests/YGFlexWrapTest.cpp @@ -0,0 +1,1735 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGFlexWrapTest.html + +#include +#include + +TEST(YogaTest, wrap_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 30); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 30); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 30); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 30); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 30); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 30); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_row_align_items_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexEnd); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 30); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_row_align_items_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 30); + YGNodeInsertChild(root, root_child3, 3); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child3)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_wrap_children_with_min_main_overriding_flex_basis) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetMinWidth(root_child0, 55); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1, 50); + YGNodeStyleSetMinWidth(root_child1, 55); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_wrap_wrap_to_child_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root_child0, YGAlignFlexStart); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 100); + YGNodeStyleSetHeight(root_child0_child0_child0, 100); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 100); + YGNodeStyleSetHeight(root_child1, 100); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_wrap_align_stretch_fits_one_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 150); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_row_align_content_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_row_align_content_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignCenter); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_row_single_line_different_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 300); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(270, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(240, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(210, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_row_align_content_stretch) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignStretch); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_row_align_content_space_around) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignContent(root, YGAlignSpaceAround); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_reverse_column_fixed_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexWrap(root, YGWrapWrapReverse); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 30); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 30); + YGNodeStyleSetHeight(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 30); + YGNodeStyleSetHeight(root_child2, 30); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child3, 30); + YGNodeStyleSetHeight(root_child3, 40); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child4, 30); + YGNodeStyleSetHeight(root_child4, 50); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(170, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(170, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(170, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(170, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrapped_row_within_align_items_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 150); + YGNodeStyleSetHeight(root_child0_child0, 80); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1, 80); + YGNodeStyleSetHeight(root_child0_child1, 80); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrapped_row_within_align_items_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 150); + YGNodeStyleSetHeight(root_child0_child0, 80); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1, 80); + YGNodeStyleSetHeight(root_child0_child1, 80); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrapped_row_within_align_items_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexEnd); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 150); + YGNodeStyleSetHeight(root_child0_child0, 80); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1, 80); + YGNodeStyleSetHeight(root_child0_child1, 80); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrapped_column_max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignContent(root, YGAlignCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 700); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 500); + YGNodeStyleSetMaxHeight(root_child0, 200); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child1, YGEdgeLeft, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeTop, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeRight, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeBottom, 20); + YGNodeStyleSetWidth(root_child1, 200); + YGNodeStyleSetHeight(root_child1, 200); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 100); + YGNodeStyleSetHeight(root_child2, 100); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(420, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(350, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrapped_column_max_height_flex) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignContent(root, YGAlignCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetFlexWrap(root, YGWrapWrap); + YGNodeStyleSetWidth(root, 700); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 0); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 500); + YGNodeStyleSetMaxHeight(root_child0, 200); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexShrink(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 0); + YGNodeStyleSetMargin(root_child1, YGEdgeLeft, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeTop, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeRight, 20); + YGNodeStyleSetMargin(root_child1, YGEdgeBottom, 20); + YGNodeStyleSetWidth(root_child1, 200); + YGNodeStyleSetHeight(root_child1, 200); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 100); + YGNodeStyleSetHeight(root_child2, 100); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(700, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_nodes_with_content_sizing_overflowing_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeStyleSetWidth(root_child0, 85); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 40); + YGNodeStyleSetHeight(root_child0_child0_child0, 40); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0_child1, YGEdgeRight, 10); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + + const YGNodeRef root_child0_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1_child0, 40); + YGNodeStyleSetHeight(root_child0_child1_child0, 40); + YGNodeInsertChild(root_child0_child1, root_child0_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(85, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(415, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(85, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, wrap_nodes_with_content_sizing_margin_cross) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(root_child0, YGWrapWrap); + YGNodeStyleSetWidth(root_child0, 70); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 40); + YGNodeStyleSetHeight(root_child0_child0_child0, 40); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0_child1, YGEdgeTop, 10); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + + const YGNodeRef root_child0_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1_child0, 40); + YGNodeStyleSetHeight(root_child0_child1_child0, 40); + YGNodeInsertChild(root_child0_child1, root_child0_child1_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(430, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(70, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child0_child1_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0_child1_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGHadOverflowTest.cpp b/Sources/yoga-tests/YGHadOverflowTest.cpp new file mode 100644 index 00000000..47dc2809 --- /dev/null +++ b/Sources/yoga-tests/YGHadOverflowTest.cpp @@ -0,0 +1,127 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include +#include + +using namespace ::testing; + +class YogaTest_HadOverflowTests : public Test { +protected: + YogaTest_HadOverflowTests() { + config = YGConfigNew(); + root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetFlexWrap(root, YGWrapNoWrap); + } + + ~YogaTest_HadOverflowTests() { + YGNodeFreeRecursive(root); + YGConfigFree(config); + } + + YGNodeRef root; + YGConfigRef config; +}; + +TEST_F(YogaTest_HadOverflowTests, children_overflow_no_wrap_and_no_flex_children) { + const YGNodeRef child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child0, 80); + YGNodeStyleSetHeight(child0, 40); + YGNodeStyleSetMargin(child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(child0, YGEdgeBottom, 15); + YGNodeInsertChild(root, child0, 0); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1, 80); + YGNodeStyleSetHeight(child1, 40); + YGNodeStyleSetMargin(child1, YGEdgeBottom, 5); + YGNodeInsertChild(root, child1, 1); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_TRUE(YGNodeLayoutGetHadOverflow(root)); +} + +TEST_F(YogaTest_HadOverflowTests, spacing_overflow_no_wrap_and_no_flex_children) { + const YGNodeRef child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child0, 80); + YGNodeStyleSetHeight(child0, 40); + YGNodeStyleSetMargin(child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, child0, 0); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1, 80); + YGNodeStyleSetHeight(child1, 40); + YGNodeStyleSetMargin(child1, YGEdgeBottom, 5); + YGNodeInsertChild(root, child1, 1); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_TRUE(YGNodeLayoutGetHadOverflow(root)); +} + +TEST_F(YogaTest_HadOverflowTests, no_overflow_no_wrap_and_flex_children) { + const YGNodeRef child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child0, 80); + YGNodeStyleSetHeight(child0, 40); + YGNodeStyleSetMargin(child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, child0, 0); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1, 80); + YGNodeStyleSetHeight(child1, 40); + YGNodeStyleSetMargin(child1, YGEdgeBottom, 5); + YGNodeStyleSetFlexShrink(child1, 1); + YGNodeInsertChild(root, child1, 1); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_FALSE(YGNodeLayoutGetHadOverflow(root)); +} + +TEST_F(YogaTest_HadOverflowTests, hadOverflow_gets_reset_if_not_logger_valid) { + const YGNodeRef child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child0, 80); + YGNodeStyleSetHeight(child0, 40); + YGNodeStyleSetMargin(child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, child0, 0); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1, 80); + YGNodeStyleSetHeight(child1, 40); + YGNodeStyleSetMargin(child1, YGEdgeBottom, 5); + YGNodeInsertChild(root, child1, 1); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_TRUE(YGNodeLayoutGetHadOverflow(root)); + + YGNodeStyleSetFlexShrink(child1, 1); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_FALSE(YGNodeLayoutGetHadOverflow(root)); +} + +TEST_F(YogaTest_HadOverflowTests, spacing_overflow_in_nested_nodes) { + const YGNodeRef child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child0, 80); + YGNodeStyleSetHeight(child0, 40); + YGNodeStyleSetMargin(child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, child0, 0); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1, 80); + YGNodeStyleSetHeight(child1, 40); + YGNodeInsertChild(root, child1, 1); + const YGNodeRef child1_1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(child1_1, 80); + YGNodeStyleSetHeight(child1_1, 40); + YGNodeStyleSetMargin(child1_1, YGEdgeBottom, 5); + YGNodeInsertChild(child1, child1_1, 0); + + YGNodeCalculateLayout(root, 200, 100, YGDirectionLTR); + + ASSERT_TRUE(YGNodeLayoutGetHadOverflow(root)); +} diff --git a/Sources/yoga-tests/YGInfiniteHeightTest.cpp b/Sources/yoga-tests/YGInfiniteHeightTest.cpp new file mode 100644 index 00000000..a557b3d9 --- /dev/null +++ b/Sources/yoga-tests/YGInfiniteHeightTest.cpp @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +// This test isn't correct from the Flexbox standard standpoint, +// because percentages are calculated with parent constraints. +// However, we need to make sure we fail gracefully in this case, not returning NaN +TEST(YogaTest, percent_absolute_position_infinite_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 300); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 300); + YGNodeStyleSetHeight(root_child0, 300); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child1, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child1, YGEdgeLeft, 20); + YGNodeStyleSetPositionPercent(root_child1, YGEdgeTop, 20); + YGNodeStyleSetWidthPercent(root_child1, 20); + YGNodeStyleSetHeightPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGJustifyContentTest.cpp b/Sources/yoga-tests/YGJustifyContentTest.cpp new file mode 100644 index 00000000..e5d42168 --- /dev/null +++ b/Sources/yoga-tests/YGJustifyContentTest.cpp @@ -0,0 +1,999 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGJustifyContentTest.html + +#include +#include + +TEST(YogaTest, justify_content_row_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(82, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(82, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(56, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(56, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_space_between) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceBetween); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_space_around) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceAround); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_flex_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_flex_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(82, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(82, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(56, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(56, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_space_between) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceBetween); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_space_around) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceAround); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_min_width_and_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetMargin(root, YGEdgeLeft, 100); + YGNodeStyleSetMinWidth(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_max_width_and_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetMargin(root, YGEdgeLeft, 100); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMaxWidth(root, 80); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_min_height_and_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetMargin(root, YGEdgeTop, 100); + YGNodeStyleSetMinHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_colunn_max_height_and_margin) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetMargin(root, YGEdgeTop, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 80); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 20); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_column_space_evenly) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceEvenly); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(18, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(74, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(18, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(74, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_row_space_evenly) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceEvenly); + YGNodeStyleSetWidth(root, 102); + YGNodeStyleSetHeight(root, 102); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(26, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(51, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(77, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(102, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(77, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(51, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(26, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGLayoutDiffingTest.cpp b/Sources/yoga-tests/YGLayoutDiffingTest.cpp new file mode 100644 index 00000000..a04e87df --- /dev/null +++ b/Sources/yoga-tests/YGLayoutDiffingTest.cpp @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +TEST(YogaTest, assert_layout_trees_are_same) { + YGConfig* config = YGConfigNew(); + YGConfigSetUseLegacyStretchBehaviour(config, true); + const YGNodeRef root1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root1, 500); + YGNodeStyleSetHeight(root1, 500); + + const YGNodeRef root1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root1_child0, YGAlignFlexStart); + YGNodeInsertChild(root1, root1_child0, 0); + + const YGNodeRef root1_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root1_child0_child0, 1); + YGNodeStyleSetFlexShrink(root1_child0_child0, 1); + YGNodeInsertChild(root1_child0, root1_child0_child0, 0); + + const YGNodeRef root1_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root1_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root1_child0_child0_child0, 1); + YGNodeInsertChild(root1_child0_child0, root1_child0_child0_child0, 0); + + const int32_t cal1_configInstanceCount = YGConfigGetInstanceCount(); + const int32_t cal1_nodeInstanceCount = YGNodeGetInstanceCount(); + + YGNodeCalculateLayout(root1, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(YGConfigGetInstanceCount(), cal1_configInstanceCount); + ASSERT_EQ(YGNodeGetInstanceCount(), cal1_nodeInstanceCount); + + const YGNodeRef root2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root2, 500); + YGNodeStyleSetHeight(root2, 500); + + const YGNodeRef root2_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root2_child0, YGAlignFlexStart); + YGNodeInsertChild(root2, root2_child0, 0); + + const YGNodeRef root2_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root2_child0_child0, 1); + YGNodeStyleSetFlexShrink(root2_child0_child0, 1); + YGNodeInsertChild(root2_child0, root2_child0_child0, 0); + + const YGNodeRef root2_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root2_child0_child0_child0, 1); + YGNodeStyleSetFlexShrink(root2_child0_child0_child0, 1); + YGNodeInsertChild(root2_child0_child0, root2_child0_child0_child0, 0); + + const int32_t cal2_configInstanceCount = YGConfigGetInstanceCount(); + const int32_t cal2_nodeInstanceCount = YGNodeGetInstanceCount(); + + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(YGConfigGetInstanceCount(), cal2_configInstanceCount); + ASSERT_EQ(YGNodeGetInstanceCount(), cal2_nodeInstanceCount); + + ASSERT_TRUE(YGNodeLayoutGetDidUseLegacyFlag(root1)); + ASSERT_TRUE(YGNodeLayoutGetDidUseLegacyFlag(root2)); + ASSERT_TRUE(root1->isLayoutTreeEqualToNode(*root2)); + + YGNodeStyleSetAlignItems(root2, YGAlignFlexEnd); + + const int32_t cal3_configInstanceCount = YGConfigGetInstanceCount(); + const int32_t cal3_nodeInstanceCount = YGNodeGetInstanceCount(); + + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(YGConfigGetInstanceCount(), cal3_configInstanceCount); + ASSERT_EQ(YGNodeGetInstanceCount(), cal3_nodeInstanceCount); + + ASSERT_FALSE(root1->isLayoutTreeEqualToNode(*root2)); + + YGNodeFreeRecursive(root1); + YGNodeFreeRecursive(root2); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGLoggerTest.cpp b/Sources/yoga-tests/YGLoggerTest.cpp new file mode 100644 index 00000000..1b84ee6b --- /dev/null +++ b/Sources/yoga-tests/YGLoggerTest.cpp @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +namespace { +char writeBuffer[4096]; +int _unmanagedLogger(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + return vsnprintf(writeBuffer + strlen(writeBuffer), + sizeof(writeBuffer) - strlen(writeBuffer), + format, + args); +} +} + +TEST(YogaTest, logger_default_node_should_print_no_style_info) { + writeBuffer[0] = '\0'; + const YGConfigRef config = YGConfigNew(); + YGConfigSetLogger(config, _unmanagedLogger); + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeCalculateLayout(root, YGUnitUndefined, YGUnitUndefined, YGDirectionLTR); + YGNodePrint(root, + (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + YGConfigSetLogger(config, NULL); + YGNodeFree(root); + + const char *expected = "
"; + ASSERT_STREQ(expected, writeBuffer); +} + +TEST(YogaTest, logger_node_with_percentage_absolute_position_and_margin) { + writeBuffer[0] = '\0'; + const YGConfigRef config = YGConfigNew(); + YGConfigSetLogger(config, _unmanagedLogger); + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute); + YGNodeStyleSetWidthPercent(root, 50); + YGNodeStyleSetHeightPercent(root, 75); + YGNodeStyleSetFlex(root, 1); + YGNodeStyleSetMargin(root, YGEdgeRight, 10); + YGNodeStyleSetMarginAuto(root, YGEdgeLeft); + YGNodeCalculateLayout(root, YGUnitUndefined, YGUnitUndefined, YGDirectionLTR); + YGNodePrint(root, + (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + YGConfigSetLogger(config, NULL); + YGNodeFree(root); + + const char *expected = "
"; + ASSERT_STREQ(expected, writeBuffer); +} + +TEST(YogaTest, logger_node_with_children_should_print_indented) { + writeBuffer[0] = '\0'; + const YGConfigRef config = YGConfigNew(); + YGConfigSetLogger(config, _unmanagedLogger); + const YGNodeRef root = YGNodeNewWithConfig(config); + const YGNodeRef child0 = YGNodeNewWithConfig(config); + const YGNodeRef child1 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, child0, 0); + YGNodeInsertChild(root, child1, 1); + YGNodeCalculateLayout(root, YGUnitUndefined, YGUnitUndefined, YGDirectionLTR); + YGNodePrint(root, + (YGPrintOptions)(YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + YGConfigSetLogger(config, NULL); + YGNodeFreeRecursive(root); + + const char *expected = "
\n " + "
\n
\n
"; + ASSERT_STREQ(expected, writeBuffer); +} diff --git a/Sources/yoga-tests/YGMarginTest.cpp b/Sources/yoga-tests/YGMarginTest.cpp new file mode 100644 index 00000000..593cd2f2 --- /dev/null +++ b/Sources/yoga-tests/YGMarginTest.cpp @@ -0,0 +1,1717 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGMarginTest.html + +#include +#include + +TEST(YogaTest, margin_start) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeEnd, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_bottom) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_and_flex_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeEnd, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_and_flex_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_and_stretch_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_and_stretch_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeEnd, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_with_sibling_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeEnd, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_with_sibling_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(55, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_bottom) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeBottom); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_bottom_and_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeBottom); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_bottom_and_top_justify_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeBottom); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_mutiple_children_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child1, YGEdgeTop); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_mutiple_children_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child1, YGEdgeRight); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_and_right_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_and_right) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_start_and_end_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeStart); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeEnd); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_start_and_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeStart); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeEnd); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_and_right_column_and_center) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_right) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_and_right_strech) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_top_and_bottom_strech) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeBottom); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_should_not_be_part_of_max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 250); + YGNodeStyleSetHeight(root, 250); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 20); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeStyleSetMaxHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_should_not_be_part_of_max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 250); + YGNodeStyleSetHeight(root, 250); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 20); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetMaxWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_right_child_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 72); + YGNodeStyleSetHeight(root_child0, 72); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_child_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetWidth(root_child0, 72); + YGNodeStyleSetHeight(root_child0, 72); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_fix_left_auto_right_child_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeRight); + YGNodeStyleSetWidth(root_child0, 72); + YGNodeStyleSetHeight(root_child0, 72); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_fix_right_child_bigger_than_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 52); + YGNodeStyleSetHeight(root, 52); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 10); + YGNodeStyleSetWidth(root_child0, 72); + YGNodeStyleSetHeight(root_child0, 72); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(72, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_top_stretching_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 0); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeTop); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, margin_auto_left_stretching_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 0); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGMeasureCacheTest.cpp b/Sources/yoga-tests/YGMeasureCacheTest.cpp new file mode 100644 index 00000000..91f18911 --- /dev/null +++ b/Sources/yoga-tests/YGMeasureCacheTest.cpp @@ -0,0 +1,176 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +static YGSize _measureMax(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + int* measureCount = (int*)node->getContext(); + (*measureCount)++; + + return YGSize{ + .width = widthMode == YGMeasureModeUndefined ? 10 : width, + .height = heightMode == YGMeasureModeUndefined ? 10 : height, + }; +} + +static YGSize _measureMin(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + int* measureCount = (int*)node->getContext(); + *measureCount = *measureCount + 1; + return YGSize{ + .width = + widthMode == YGMeasureModeUndefined || (widthMode == YGMeasureModeAtMost && width > 10) + ? 10 + : width, + .height = + heightMode == YGMeasureModeUndefined || (heightMode == YGMeasureModeAtMost && height > 10) + ? 10 + : height, + }; +} + +static YGSize _measure_84_49(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + int* measureCount = (int*)node->getContext(); + if (measureCount) { + (*measureCount)++; + } + + return YGSize{ + .width = 84.f, .height = 49.f, + }; +} + +TEST(YogaTest, measure_once_single_flexible_child) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + int measureCount = 0; + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measureMax); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, remeasure_with_same_exact_width_larger_than_needed_height) { + const YGNodeRef root = YGNodeNew(); + + const YGNodeRef root_child0 = YGNodeNew(); + int measureCount = 0; + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measureMin); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + YGNodeCalculateLayout(root, 100, 50, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, remeasure_with_same_atmost_width_larger_than_needed_height) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + + const YGNodeRef root_child0 = YGNodeNew(); + int measureCount = 0; + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measureMin); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + YGNodeCalculateLayout(root, 100, 50, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, remeasure_with_computed_width_larger_than_needed_height) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + + const YGNodeRef root_child0 = YGNodeNew(); + int measureCount = 0; + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measureMin); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + YGNodeStyleSetAlignItems(root, YGAlignStretch); + YGNodeCalculateLayout(root, 10, 50, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, remeasure_with_atmost_computed_width_undefined_height) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + + const YGNodeRef root_child0 = YGNodeNew(); + int measureCount = 0; + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measureMin); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 100, YGUndefined, YGDirectionLTR); + YGNodeCalculateLayout(root, 10, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, remeasure_with_already_measured_value_smaller_but_still_float_equal) { + int measureCount = 0; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 288.f); + YGNodeStyleSetHeight(root, 288.f); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetPadding(root_child0, YGEdgeAll, 2.88f); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNew(); + root_child0_child0->setContext(&measureCount); + root_child0_child0->setMeasureFunc(_measure_84_49); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + + ASSERT_EQ(1, measureCount); +} diff --git a/Sources/yoga-tests/YGMeasureModeTest.cpp b/Sources/yoga-tests/YGMeasureModeTest.cpp new file mode 100644 index 00000000..cbe1985a --- /dev/null +++ b/Sources/yoga-tests/YGMeasureModeTest.cpp @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +struct _MeasureConstraint { + float width; + YGMeasureMode widthMode; + float height; + YGMeasureMode heightMode; +}; + +struct _MeasureConstraintList { + uint32_t length; + struct _MeasureConstraint *constraints; +}; + +static YGSize _measure(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + struct _MeasureConstraintList* constraintList = + (struct _MeasureConstraintList*)node->getContext(); + struct _MeasureConstraint *constraints = constraintList->constraints; + uint32_t currentIndex = constraintList->length; + (&constraints[currentIndex])->width = width; + (&constraints[currentIndex])->widthMode = widthMode; + (&constraints[currentIndex])->height = height; + (&constraints[currentIndex])->heightMode = heightMode; + constraintList->length = currentIndex + 1; + + return YGSize{ + .width = widthMode == YGMeasureModeUndefined ? 10 : width, + .height = heightMode == YGMeasureModeUndefined ? 10 : width, + }; +} + +TEST(YogaTest, exactly_measure_stretched_child_column) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + // root_child0->setContext(&constraintList); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + // root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].width); + ASSERT_EQ(YGMeasureModeExactly, constraintList.constraints[0].widthMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, exactly_measure_stretched_child_row) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + // root_child0->setContext(&constraintList); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeExactly, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, at_most_main_axis_column) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, at_most_cross_axis_column) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].width); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].widthMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, at_most_main_axis_row) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].width); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].widthMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, at_most_cross_axis_row) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, flex_child) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(2, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].heightMode); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[1].height); + ASSERT_EQ(YGMeasureModeExactly, constraintList.constraints[1].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, flex_child_with_flex_basis) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 0); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeExactly, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, overflow_scroll_column) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetOverflow(root, YGOverflowScroll); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].width); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].widthMode); + + ASSERT_TRUE(YGFloatIsUndefined(constraintList.constraints[0].height)); + ASSERT_EQ(YGMeasureModeUndefined, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, overflow_scroll_row) { + struct _MeasureConstraintList constraintList = _MeasureConstraintList{ + .length = 0, + .constraints = (struct _MeasureConstraint *) malloc(10 * sizeof(struct _MeasureConstraint)), + }; + + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetOverflow(root, YGOverflowScroll); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&constraintList); + root_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, constraintList.length); + + ASSERT_TRUE(YGFloatIsUndefined(constraintList.constraints[0].width)); + ASSERT_EQ(YGMeasureModeUndefined, constraintList.constraints[0].widthMode); + + ASSERT_FLOAT_EQ(100, constraintList.constraints[0].height); + ASSERT_EQ(YGMeasureModeAtMost, constraintList.constraints[0].heightMode); + + free(constraintList.constraints); + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGMeasureTest.cpp b/Sources/yoga-tests/YGMeasureTest.cpp new file mode 100644 index 00000000..aea2cd59 --- /dev/null +++ b/Sources/yoga-tests/YGMeasureTest.cpp @@ -0,0 +1,689 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +static YGSize _measure(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + int* measureCount = (int*)node->getContext(); + if (measureCount) { + (*measureCount)++; + } + + return YGSize{ + .width = 10, .height = 10, + }; +} + +static YGSize _simulate_wrapping_text(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + if (widthMode == YGMeasureModeUndefined || width >= 68) { + return YGSize{.width = 68, .height = 16}; + } + + return YGSize{ + .width = 50, .height = 32, + }; +} + +static YGSize _measure_assert_negative(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + EXPECT_GE(width, 0); + EXPECT_GE(height, 0); + + return YGSize{ + .width = 0, .height = 0, + }; +} + +TEST(YogaTest, dont_measure_single_grow_shrink_child) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, measure_absolute_child_with_no_constraints) { + const YGNodeRef root = YGNodeNew(); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeInsertChild(root, root_child0, 0); + + int measureCount = 0; + + const YGNodeRef root_child0_child0 = YGNodeNew(); + YGNodeStyleSetPositionType(root_child0_child0, YGPositionTypeAbsolute); + root_child0_child0->setContext(&measureCount); + root_child0_child0->setMeasureFunc(_measure); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(1, measureCount); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dont_measure_when_min_equals_max) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetMinWidth(root_child0, 10); + YGNodeStyleSetMaxWidth(root_child0, 10); + YGNodeStyleSetMinHeight(root_child0, 10); + YGNodeStyleSetMaxHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, measureCount); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dont_measure_when_min_equals_max_percentages) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetMinWidthPercent(root_child0, 10); + YGNodeStyleSetMaxWidthPercent(root_child0, 10); + YGNodeStyleSetMinHeightPercent(root_child0, 10); + YGNodeStyleSetMaxHeightPercent(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, measureCount); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + + +TEST(YogaTest, measure_nodes_with_margin_auto_and_stretch) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetMarginAuto(root_child0, YGEdgeLeft); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_EQ(490, YGNodeLayoutGetLeft(root_child0)); + EXPECT_EQ(0, YGNodeLayoutGetTop(root_child0)); + EXPECT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + EXPECT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dont_measure_when_min_equals_max_mixed_width_percent) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetMinWidthPercent(root_child0, 10); + YGNodeStyleSetMaxWidthPercent(root_child0, 10); + YGNodeStyleSetMinHeight(root_child0, 10); + YGNodeStyleSetMaxHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, measureCount); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, dont_measure_when_min_equals_max_mixed_height_percent) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + YGNodeStyleSetMinWidth(root_child0, 10); + YGNodeStyleSetMaxWidth(root_child0, 10); + YGNodeStyleSetMinHeightPercent(root_child0, 10); + YGNodeStyleSetMaxHeightPercent(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, measureCount); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, measure_enough_size_should_be_in_single_line) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexStart); + root_child0->setMeasureFunc(_simulate_wrapping_text); + + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(68, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(16, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, measure_not_enough_size_should_wrap) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 55); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetAlignSelf(root_child0, YGAlignFlexStart); + // YGNodeSetMeasureFunc(root_child0, _simulate_wrapping_text); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(32, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, measure_zero_space_should_grow) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetHeight(root, 200); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetFlexGrow(root, 0); + + int measureCount = 0; + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionColumn); + YGNodeStyleSetPadding(root_child0, YGEdgeAll, 100); + root_child0->setContext(&measureCount); + root_child0->setMeasureFunc(_measure); + + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 282, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(282, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, measure_flex_direction_row_and_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetPadding(root, YGEdgeLeft, 25); + YGNodeStyleSetPadding(root, YGEdgeTop, 25); + YGNodeStyleSetPadding(root, YGEdgeRight, 25); + YGNodeStyleSetPadding(root, YGEdgeBottom, 25); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + // YGNodeSetMeasureFunc(root_child0, _simulate_wrapping_text); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_flex_direction_column_and_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetPadding(root, YGEdgeAll, 25); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + // YGNodeSetMeasureFunc(root_child0, _simulate_wrapping_text); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(32, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(57, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_flex_direction_row_no_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + // YGNodeSetMeasureFunc(root_child0, _simulate_wrapping_text); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_flex_direction_row_no_padding_align_items_flexstart) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(32, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_with_fixed_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetPadding(root, YGEdgeAll, 25); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_with_flex_shrink) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetPadding(root, YGEdgeAll, 25); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, measure_no_padding) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root, YGEdgeTop, 20); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_simulate_wrapping_text); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 5); + YGNodeStyleSetHeight(root_child1, 5); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(32, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(32, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +#if GTEST_HAS_DEATH_TEST +TEST(YogaDeathTest, cannot_add_child_to_node_with_measure_func) { + const YGNodeRef root = YGNodeNew(); + root->setMeasureFunc(_measure); + + const YGNodeRef root_child0 = YGNodeNew(); + ASSERT_DEATH(YGNodeInsertChild(root, root_child0, 0), "Cannot add child.*"); + YGNodeFree(root_child0); + YGNodeFreeRecursive(root); +} + +TEST(YogaDeathTest, cannot_add_nonnull_measure_func_to_non_leaf_node) { + const YGNodeRef root = YGNodeNew(); + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeInsertChild(root, root_child0, 0); + ASSERT_DEATH(root->setMeasureFunc(_measure), "Cannot set measure function.*"); + YGNodeFreeRecursive(root); +} + +#endif + +TEST(YogaTest, can_nullify_measure_func_on_any_node) { + const YGNodeRef root = YGNodeNew(); + YGNodeInsertChild(root, YGNodeNew(), 0); + root->setMeasureFunc(nullptr); + ASSERT_TRUE(root->getMeasure() == NULL); + YGNodeFreeRecursive(root); +} + +TEST(YogaTest, cant_call_negative_measure) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 10); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_measure_assert_negative); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(YogaTest, cant_call_negative_measure_horizontal) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 10); + YGNodeStyleSetHeight(root, 20); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_measure_assert_negative); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +static YGSize _measure_90_10(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + + return YGSize{ + .width = 90, .height = 10, + }; +} + +TEST(YogaTest, percent_with_text_node) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root, YGJustifySpaceBetween); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 80); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + root_child1->setMeasureFunc(_measure_90_10); + YGNodeStyleSetMaxWidthPercent(root_child1, 50); + YGNodeStyleSetPaddingPercent(root_child1, YGEdgeTop, 50); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(15, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGMinMaxDimensionTest.cpp b/Sources/yoga-tests/YGMinMaxDimensionTest.cpp new file mode 100644 index 00000000..515bdfdc --- /dev/null +++ b/Sources/yoga-tests/YGMinMaxDimensionTest.cpp @@ -0,0 +1,1297 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGMinMaxDimensionTest.html + +#include +#include + +TEST(YogaTest, max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMaxWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetMaxHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, min_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMinHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, min_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMinWidth(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_min_max) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, align_items_min_max) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetMinWidth(root, 100); + YGNodeStyleSetMaxWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 60); + YGNodeStyleSetHeight(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, justify_content_overflow_min_max) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 110); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 50); + YGNodeStyleSetHeight(root_child2, 50); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(110, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_to_min) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_in_at_most_container) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexBasis(root_child0_child0, 0); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 0); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_min_max_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetMaxWidth(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetHeight(root_child0_child0, 20); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetMaxWidth(root_child0, 300); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetHeight(root_child0_child0, 20); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_root_ignored) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root, 1); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 200); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 100); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_root_minimized) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMinHeight(root, 100); + YGNodeStyleSetMaxHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMinHeight(root_child0, 100); + YGNodeStyleSetMaxHeight(root_child0, 500); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexBasis(root_child0_child0, 200); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0_child1, 100); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_height_maximized) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMinHeight(root_child0, 100); + YGNodeStyleSetMaxHeight(root_child0, 500); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexBasis(root_child0_child0, 200); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0_child1, 100); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_min_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetMinWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_min_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetMinHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_max_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetMaxWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child0_child0, 1); + YGNodeStyleSetFlexBasis(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child1, 50); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, flex_grow_within_constrained_max_column) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetMaxHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child1, 50); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, child_min_max_width_flexing) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 120); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 0); + YGNodeStyleSetMinWidth(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 50); + YGNodeStyleSetMaxWidth(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, min_width_overrides_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetMinWidth(root, 100); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, max_width_overrides_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetMaxWidth(root, 100); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, min_height_overrides_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root, 50); + YGNodeStyleSetMinHeight(root, 100); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, max_height_overrides_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root, 200); + YGNodeStyleSetMaxHeight(root, 100); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, min_max_percent_no_width_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMinWidthPercent(root_child0, 10); + YGNodeStyleSetMaxWidthPercent(root_child0, 10); + YGNodeStyleSetMinHeightPercent(root_child0, 10); + YGNodeStyleSetMaxHeightPercent(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGNodeChildTest.cpp b/Sources/yoga-tests/YGNodeChildTest.cpp new file mode 100644 index 00000000..9d6bb486 --- /dev/null +++ b/Sources/yoga-tests/YGNodeChildTest.cpp @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, reset_layout_when_child_removed) { + const YGNodeRef root = YGNodeNew(); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeRemoveChild(root, root_child0); + + ASSERT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetWidth(root_child0))); + ASSERT_TRUE(YGFloatIsUndefined(YGNodeLayoutGetHeight(root_child0))); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGPaddingTest.cpp b/Sources/yoga-tests/YGPaddingTest.cpp new file mode 100644 index 00000000..f0d37527 --- /dev/null +++ b/Sources/yoga-tests/YGPaddingTest.cpp @@ -0,0 +1,258 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGPaddingTest.html + +#include +#include + +TEST(YogaTest, padding_no_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root, YGEdgeTop, 10); + YGNodeStyleSetPadding(root, YGEdgeRight, 10); + YGNodeStyleSetPadding(root, YGEdgeBottom, 10); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetHeight(root)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, padding_container_match_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root, YGEdgeTop, 10); + YGNodeStyleSetPadding(root, YGEdgeRight, 10); + YGNodeStyleSetPadding(root, YGEdgeBottom, 10); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, padding_flex_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root, YGEdgeTop, 10); + YGNodeStyleSetPadding(root, YGEdgeRight, 10); + YGNodeStyleSetPadding(root, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, padding_stretch_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root, YGEdgeLeft, 10); + YGNodeStyleSetPadding(root, YGEdgeTop, 10); + YGNodeStyleSetPadding(root, YGEdgeRight, 10); + YGNodeStyleSetPadding(root, YGEdgeBottom, 10); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, padding_center_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetPadding(root, YGEdgeStart, 10); + YGNodeStyleSetPadding(root, YGEdgeEnd, 20); + YGNodeStyleSetPadding(root, YGEdgeBottom, 20); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, child_with_padding_align_end) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyFlexEnd); + YGNodeStyleSetAlignItems(root, YGAlignFlexEnd); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPadding(root_child0, YGEdgeLeft, 20); + YGNodeStyleSetPadding(root_child0, YGEdgeTop, 20); + YGNodeStyleSetPadding(root_child0, YGEdgeRight, 20); + YGNodeStyleSetPadding(root_child0, YGEdgeBottom, 20); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGPercentageTest.cpp b/Sources/yoga-tests/YGPercentageTest.cpp new file mode 100644 index 00000000..2be04595 --- /dev/null +++ b/Sources/yoga-tests/YGPercentageTest.cpp @@ -0,0 +1,1194 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGPercentageTest.html + +#include +#include + +TEST(YogaTest, percentage_width_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidthPercent(root_child0, 30); + YGNodeStyleSetHeightPercent(root_child0, 30); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_position_left_top) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 400); + YGNodeStyleSetHeight(root, 400); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeTop, 20); + YGNodeStyleSetWidthPercent(root_child0, 45); + YGNodeStyleSetHeightPercent(root_child0, 55); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(220, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(260, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(180, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(220, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_position_bottom_right) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 500); + YGNodeStyleSetHeight(root, 500); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeRight, 20); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeBottom, 10); + YGNodeStyleSetWidthPercent(root_child0, 55); + YGNodeStyleSetHeightPercent(root_child0, 15); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(-100, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(275, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(-50, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(275, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_cross) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetFlexBasisPercent(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_cross_min_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMinHeightPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 2); + YGNodeStyleSetMinHeightPercent(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(140, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_main_max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 10); + YGNodeStyleSetMaxHeightPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 10); + YGNodeStyleSetMaxHeightPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(148, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(148, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(52, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(148, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_cross_max_height) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 10); + YGNodeStyleSetMaxHeightPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 10); + YGNodeStyleSetMaxHeightPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_main_max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 15); + YGNodeStyleSetMaxWidthPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 10); + YGNodeStyleSetMaxWidthPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_cross_max_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 10); + YGNodeStyleSetMaxWidthPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 15); + YGNodeStyleSetMaxWidthPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_main_min_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 15); + YGNodeStyleSetMinWidthPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 10); + YGNodeStyleSetMinWidthPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(120, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(80, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_flex_basis_cross_min_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 10); + YGNodeStyleSetMinWidthPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 15); + YGNodeStyleSetMinWidthPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_multiple_nested_with_padding_margin_and_percentage_values) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasisPercent(root_child0, 10); + YGNodeStyleSetMargin(root_child0, YGEdgeLeft, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeRight, 5); + YGNodeStyleSetMargin(root_child0, YGEdgeBottom, 5); + YGNodeStyleSetPadding(root_child0, YGEdgeLeft, 3); + YGNodeStyleSetPadding(root_child0, YGEdgeTop, 3); + YGNodeStyleSetPadding(root_child0, YGEdgeRight, 3); + YGNodeStyleSetPadding(root_child0, YGEdgeBottom, 3); + YGNodeStyleSetMinWidthPercent(root_child0, 60); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeLeft, 5); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeTop, 5); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeRight, 5); + YGNodeStyleSetMargin(root_child0_child0, YGEdgeBottom, 5); + YGNodeStyleSetPaddingPercent(root_child0_child0, YGEdgeLeft, 3); + YGNodeStyleSetPaddingPercent(root_child0_child0, YGEdgeTop, 3); + YGNodeStyleSetPaddingPercent(root_child0_child0, YGEdgeRight, 3); + YGNodeStyleSetPaddingPercent(root_child0_child0, YGEdgeBottom, 3); + YGNodeStyleSetWidthPercent(root_child0_child0, 50); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetMarginPercent(root_child0_child0_child0, YGEdgeLeft, 5); + YGNodeStyleSetMarginPercent(root_child0_child0_child0, YGEdgeTop, 5); + YGNodeStyleSetMarginPercent(root_child0_child0_child0, YGEdgeRight, 5); + YGNodeStyleSetMarginPercent(root_child0_child0_child0, YGEdgeBottom, 5); + YGNodeStyleSetPadding(root_child0_child0_child0, YGEdgeLeft, 3); + YGNodeStyleSetPadding(root_child0_child0_child0, YGEdgeTop, 3); + YGNodeStyleSetPadding(root_child0_child0_child0, YGEdgeRight, 3); + YGNodeStyleSetPadding(root_child0_child0_child0, YGEdgeBottom, 3); + YGNodeStyleSetWidthPercent(root_child0_child0_child0, 45); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 4); + YGNodeStyleSetFlexBasisPercent(root_child1, 15); + YGNodeStyleSetMinWidthPercent(root_child1, 20); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(190, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(48, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(8, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(8, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(6, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(58, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(142, YGNodeLayoutGetHeight(root_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(5, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(190, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(48, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(8, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(92, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(46, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(36, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(6, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(58, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(142, YGNodeLayoutGetHeight(root_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_margin_should_calculate_based_only_on_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetMarginPercent(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetMarginPercent(root_child0, YGEdgeTop, 10); + YGNodeStyleSetMarginPercent(root_child0, YGEdgeRight, 10); + YGNodeStyleSetMarginPercent(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 10); + YGNodeStyleSetHeight(root_child0_child0, 10); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(160, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_padding_should_calculate_based_only_on_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetPaddingPercent(root_child0, YGEdgeLeft, 10); + YGNodeStyleSetPaddingPercent(root_child0, YGEdgeTop, 10); + YGNodeStyleSetPaddingPercent(root_child0, YGEdgeRight, 10); + YGNodeStyleSetPaddingPercent(root_child0, YGEdgeBottom, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 10); + YGNodeStyleSetHeight(root_child0_child0, 10); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(170, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(20, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_absolute_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeLeft, 30); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeTop, 10); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_width_height_undefined_parent_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidthPercent(root_child0, 50); + YGNodeStyleSetHeightPercent(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percent_within_flex_grow) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 350); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidthPercent(root_child1_child0, 100); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child2, 100); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(350, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(350, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(250, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percentage_container_in_wrapping_container) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetJustifyContent(root, YGJustifyCenter); + YGNodeStyleSetAlignItems(root, YGAlignCenter); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0_child0, YGFlexDirectionRow); + YGNodeStyleSetJustifyContent(root_child0_child0, YGJustifyCenter); + YGNodeStyleSetWidthPercent(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child0, 50); + YGNodeStyleSetHeight(root_child0_child0_child0, 50); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0); + + const YGNodeRef root_child0_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0_child1, 50); + YGNodeStyleSetHeight(root_child0_child0_child1, 50); + YGNodeInsertChild(root_child0_child0, root_child0_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, percent_absolute_position) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 60); + YGNodeStyleSetHeight(root, 50); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root_child0, YGFlexDirectionRow); + YGNodeStyleSetPositionType(root_child0, YGPositionTypeAbsolute); + YGNodeStyleSetPositionPercent(root_child0, YGEdgeLeft, 50); + YGNodeStyleSetWidthPercent(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidthPercent(root_child0_child0, 100); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidthPercent(root_child0_child1, 100); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(-60, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child1)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGPersistenceTest.cpp b/Sources/yoga-tests/YGPersistenceTest.cpp new file mode 100644 index 00000000..9df861a8 --- /dev/null +++ b/Sources/yoga-tests/YGPersistenceTest.cpp @@ -0,0 +1,250 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGPercentageTest.html + +#include +#include + +TEST(YogaTest, cloning_shared_root) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + const YGNodeRef root2 = YGNodeClone(root); + YGNodeStyleSetWidth(root2, 100); + + ASSERT_EQ(2, YGNodeGetChildCount(root2)); + // The children should have referential equality at this point. + ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0)); + ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1)); + + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(2, YGNodeGetChildCount(root2)); + // Relayout with no changed input should result in referential equality. + ASSERT_EQ(root_child0, YGNodeGetChild(root2, 0)); + ASSERT_EQ(root_child1, YGNodeGetChild(root2, 1)); + + YGNodeStyleSetWidth(root2, 150); + YGNodeStyleSetHeight(root2, 200); + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(2, YGNodeGetChildCount(root2)); + // Relayout with changed input should result in cloned children. + const YGNodeRef root2_child0 = YGNodeGetChild(root2, 0); + const YGNodeRef root2_child1 = YGNodeGetChild(root2, 1); + ASSERT_NE(root_child0, root2_child0); + ASSERT_NE(root_child1, root2_child1); + + // Everything in the root should remain unchanged. + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + // The new root now has new layout. + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root2_child0)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child0)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetHeight(root2_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root2_child1)); + ASSERT_FLOAT_EQ(125, YGNodeLayoutGetTop(root2_child1)); + ASSERT_FLOAT_EQ(150, YGNodeLayoutGetWidth(root2_child1)); + ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(root2_child1)); + + YGNodeFreeRecursive(root2); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, mutating_children_of_a_clone_clones) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + ASSERT_EQ(0, YGNodeGetChildCount(root)); + + const YGNodeRef root2 = YGNodeClone(root); + ASSERT_EQ(0, YGNodeGetChildCount(root2)); + + const YGNodeRef root2_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root2, root2_child0, 0); + + ASSERT_EQ(0, YGNodeGetChildCount(root)); + ASSERT_EQ(1, YGNodeGetChildCount(root2)); + + const YGNodeRef root3 = YGNodeClone(root2); + ASSERT_EQ(1, YGNodeGetChildCount(root2)); + ASSERT_EQ(1, YGNodeGetChildCount(root3)); + ASSERT_EQ(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0)); + + const YGNodeRef root3_child1 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root3, root3_child1, 1); + ASSERT_EQ(1, YGNodeGetChildCount(root2)); + ASSERT_EQ(2, YGNodeGetChildCount(root3)); + ASSERT_EQ(root3_child1, YGNodeGetChild(root3, 1)); + ASSERT_NE(YGNodeGetChild(root2, 0), YGNodeGetChild(root3, 0)); + + const YGNodeRef root4 = YGNodeClone(root3); + ASSERT_EQ(root3_child1, YGNodeGetChild(root4, 1)); + + YGNodeRemoveChild(root4, root3_child1); + ASSERT_EQ(2, YGNodeGetChildCount(root3)); + ASSERT_EQ(1, YGNodeGetChildCount(root4)); + ASSERT_NE(YGNodeGetChild(root3, 0), YGNodeGetChild(root4, 0)); + + YGNodeFreeRecursive(root4); + YGNodeFreeRecursive(root3); + YGNodeFreeRecursive(root2); + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, cloning_two_levels) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 15); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1_0, 10); + YGNodeStyleSetFlexGrow(root_child1_0, 1); + YGNodeInsertChild(root_child1, root_child1_0, 0); + + const YGNodeRef root_child1_1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1_1, 25); + YGNodeInsertChild(root_child1, root_child1_1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1)); + + const YGNodeRef root2_child0 = YGNodeClone(root_child0); + const YGNodeRef root2_child1 = YGNodeClone(root_child1); + const YGNodeRef root2 = YGNodeClone(root); + + YGNodeStyleSetFlexGrow(root2_child0, 0); + YGNodeStyleSetFlexBasis(root2_child0, 40); + + YGNodeRemoveAllChildren(root2); + YGNodeInsertChild(root2, root2_child0, 0); + YGNodeInsertChild(root2, root2_child1, 1); + ASSERT_EQ(2, YGNodeGetChildCount(root2)); + + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + // Original root is unchanged + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root_child1)); + ASSERT_FLOAT_EQ(35, YGNodeLayoutGetHeight(root_child1_0)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1_1)); + + // New root has new layout at the top + ASSERT_FLOAT_EQ(40, YGNodeLayoutGetHeight(root2_child0)); + ASSERT_FLOAT_EQ(60, YGNodeLayoutGetHeight(root2_child1)); + + // The deeper children are untouched. + ASSERT_EQ(YGNodeGetChild(root2_child1, 0), root_child1_0); + ASSERT_EQ(YGNodeGetChild(root2_child1, 1), root_child1_1); + + YGNodeFreeRecursive(root2); + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, cloning_and_freeing) { + const int32_t initialInstanceCount = YGNodeGetInstanceCount(); + + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child1, 1); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + const YGNodeRef root2 = YGNodeClone(root); + + // Freeing the original root should be safe as long as we don't free its + // children. + YGNodeFree(root); + + YGNodeCalculateLayout(root2, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root2); + + YGNodeFree(root_child0); + YGNodeFree(root_child1); + + YGConfigFree(config); + + ASSERT_EQ(initialInstanceCount, YGNodeGetInstanceCount()); +} diff --git a/Sources/yoga-tests/YGRelayoutTest.cpp b/Sources/yoga-tests/YGRelayoutTest.cpp new file mode 100644 index 00000000..cbda5248 --- /dev/null +++ b/Sources/yoga-tests/YGRelayoutTest.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, dont_cache_computed_flex_basis_between_layouts) { + const YGConfigRef config = YGConfigNew(); + YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureWebFlexBasis, true); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetHeightPercent(root, 100); + YGNodeStyleSetWidthPercent(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasisPercent(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, 100, YGUndefined, YGDirectionLTR); + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, recalculate_resolvedDimonsion_onchange) { + const YGNodeRef root = YGNodeNew(); + + const YGNodeRef root_child0 = YGNodeNew(); + YGNodeStyleSetMinHeight(root_child0, 10); + YGNodeStyleSetMaxHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + YGNodeStyleSetMinHeight(root_child0, YGUndefined); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga-tests/YGRoundingFunctionTest.cpp b/Sources/yoga-tests/YGRoundingFunctionTest.cpp new file mode 100644 index 00000000..82eba44d --- /dev/null +++ b/Sources/yoga-tests/YGRoundingFunctionTest.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +TEST(YogaTest, rounding_value) { + // Test that whole numbers are rounded to whole despite ceil/floor flags + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.000001, 2.0, false, true)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.999999, 2.0, false, true)); + + // Test that numbers with fraction are rounded correctly accounting for ceil/floor flags + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.5, YGRoundValueToPixelGrid(6.01, 2.0, true, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(6.01, 2.0, false, true)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, false, false)); + ASSERT_FLOAT_EQ(6.0, YGRoundValueToPixelGrid(5.99, 2.0, true, false)); + ASSERT_FLOAT_EQ(5.5, YGRoundValueToPixelGrid(5.99, 2.0, false, true)); +} diff --git a/Sources/yoga-tests/YGRoundingMeasureFuncTest.cpp b/Sources/yoga-tests/YGRoundingMeasureFuncTest.cpp new file mode 100644 index 00000000..33820c44 --- /dev/null +++ b/Sources/yoga-tests/YGRoundingMeasureFuncTest.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +static YGSize _measureFloor(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + return YGSize{ + width = 10.2f, height = 10.2f, + }; +} + +static YGSize _measureCeil(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + return YGSize{ + width = 10.5f, height = 10.5f, + }; +} + +static YGSize _measureFractial(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + return YGSize{ + width = 0.5f, height = 0.5f, + }; +} + +TEST(YogaTest, rounding_feature_with_custom_measure_func_floor) { + const YGConfigRef config = YGConfigNew(); + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_measureFloor); + YGNodeInsertChild(root, root_child0, 0); + + YGConfigSetPointScaleFactor(config, 0.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(10.2, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10.2, YGNodeLayoutGetHeight(root_child0)); + + YGConfigSetPointScaleFactor(config, 1.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(11, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(11, YGNodeLayoutGetHeight(root_child0)); + + YGConfigSetPointScaleFactor(config, 2.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(10.5, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10.5, YGNodeLayoutGetHeight(root_child0)); + + YGConfigSetPointScaleFactor(config, 4.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10.25, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10.25, YGNodeLayoutGetHeight(root_child0)); + + YGConfigSetPointScaleFactor(config, 1.0f / 3.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(12.0, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(12.0, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_feature_with_custom_measure_func_ceil) { + const YGConfigRef config = YGConfigNew(); + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + root_child0->setMeasureFunc(_measureCeil); + YGNodeInsertChild(root, root_child0, 0); + + YGConfigSetPointScaleFactor(config, 1.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(11, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(11, YGNodeLayoutGetHeight(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_feature_with_custom_measure_and_fractial_matching_scale) { + const YGConfigRef config = YGConfigNew(); + const YGNodeRef root = YGNodeNewWithConfig(config); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root_child0, YGEdgeLeft, 73.625); + root_child0->setMeasureFunc(_measureFractial); + YGNodeInsertChild(root, root_child0, 0); + + YGConfigSetPointScaleFactor(config, 2.0f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0.5, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(0.5, YGNodeLayoutGetHeight(root_child0)); + ASSERT_FLOAT_EQ(73.5, YGNodeLayoutGetLeft(root_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGRoundingTest.cpp b/Sources/yoga-tests/YGRoundingTest.cpp new file mode 100644 index 00000000..303e9f0b --- /dev/null +++ b/Sources/yoga-tests/YGRoundingTest.cpp @@ -0,0 +1,1077 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGRoundingTest.html + +#include +#include + +TEST(YogaTest, rounding_flex_basis_flex_grow_row_width_of_100) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(34, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(67, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(67, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(34, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(33, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_flex_basis_flex_grow_row_prime_number_width) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 113); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeInsertChild(root, root_child2, 2); + + const YGNodeRef root_child3 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child3, 1); + YGNodeInsertChild(root, root_child3, 3); + + const YGNodeRef root_child4 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child4, 1); + YGNodeInsertChild(root, root_child4, 4); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(22, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(68, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(22, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(90, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(68, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(22, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(45, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetLeft(root_child3)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child3)); + ASSERT_FLOAT_EQ(22, YGNodeLayoutGetWidth(root_child3)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child3)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child4)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child4)); + ASSERT_FLOAT_EQ(23, YGNodeLayoutGetWidth(root_child4)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child4)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_flex_basis_flex_shrink_row) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 101); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexShrink(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child1, 25); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(root_child2, 25); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(101, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(51, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(51, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(76, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(101, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(50, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(51, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_flex_basis_overrides_main_size) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 113); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_total_fractial) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 87.4f); + YGNodeStyleSetHeight(root, 113.4f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 0.7f); + YGNodeStyleSetFlexBasis(root_child0, 50.3f); + YGNodeStyleSetHeight(root_child0, 20.3f); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1.6f); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1.1f); + YGNodeStyleSetHeight(root_child2, 10.7f); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_total_fractial_nested) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 87.4f); + YGNodeStyleSetHeight(root, 113.4f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 0.7f); + YGNodeStyleSetFlexBasis(root_child0, 50.3f); + YGNodeStyleSetHeight(root_child0, 20.3f); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child0, 1); + YGNodeStyleSetFlexBasis(root_child0_child0, 0.3f); + YGNodeStyleSetPosition(root_child0_child0, YGEdgeBottom, 13.3f); + YGNodeStyleSetHeight(root_child0_child0, 9.9f); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + + const YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0_child1, 4); + YGNodeStyleSetFlexBasis(root_child0_child1, 0.3f); + YGNodeStyleSetPosition(root_child0_child1, YGEdgeTop, 13.3f); + YGNodeStyleSetHeight(root_child0_child1, 1.1f); + YGNodeInsertChild(root_child0, root_child0_child1, 1); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1.6f); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1.1f); + YGNodeStyleSetHeight(root_child2, 10.7f); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(-13, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(47, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(-13, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(12, YGNodeLayoutGetHeight(root_child0_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetTop(root_child0_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child0_child1)); + ASSERT_FLOAT_EQ(47, YGNodeLayoutGetHeight(root_child0_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(59, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(30, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(87, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_fractial_input_1) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 113.4f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_fractial_input_2) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 113.6f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(114, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(114, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_fractial_input_3) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root, YGEdgeTop, 0.3f); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 113.4f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(114, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(114, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(65, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_fractial_input_4) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetPosition(root, YGEdgeTop, 0.7f); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 113.4f); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetFlexBasis(root_child0, 50); + YGNodeStyleSetHeight(root_child0, 20); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(1, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(1, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(113, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(64, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(25, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(89, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(24, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_inner_node_controversy_horizontal) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 320); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetHeight(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeight(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child0, 1); + YGNodeStyleSetHeight(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeight(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_inner_node_controversy_vertical) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(root, 320); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetWidth(root_child0, 10); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetWidth(root_child1, 10); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child0, 1); + YGNodeStyleSetWidth(root_child1_child0, 10); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetWidth(root_child2, 10); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, rounding_inner_node_controversy_combined) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 640); + YGNodeStyleSetHeight(root, 320); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child0, 1); + YGNodeStyleSetHeightPercent(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1, 1); + YGNodeStyleSetHeightPercent(root_child1, 100); + YGNodeInsertChild(root, root_child1, 1); + + const YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child0, 1); + YGNodeStyleSetWidthPercent(root_child1_child0, 100); + YGNodeInsertChild(root_child1, root_child1_child0, 0); + + const YGNodeRef root_child1_child1 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child1, 1); + YGNodeStyleSetWidthPercent(root_child1_child1, 100); + YGNodeInsertChild(root_child1, root_child1_child1, 1); + + const YGNodeRef root_child1_child1_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child1_child0, 1); + YGNodeStyleSetWidthPercent(root_child1_child1_child0, 100); + YGNodeInsertChild(root_child1_child1, root_child1_child1_child0, 0); + + const YGNodeRef root_child1_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child1_child2, 1); + YGNodeStyleSetWidthPercent(root_child1_child2, 100); + YGNodeInsertChild(root_child1, root_child1_child2, 2); + + const YGNodeRef root_child2 = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexGrow(root_child2, 1); + YGNodeStyleSetHeightPercent(root_child2, 100); + YGNodeInsertChild(root, root_child2, 2); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(640, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(427, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child2)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(640, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(427, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetLeft(root_child1)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child0)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetTop(root_child1_child1)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child1)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child1)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child1_child0)); + ASSERT_FLOAT_EQ(106, YGNodeLayoutGetHeight(root_child1_child1_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetTop(root_child1_child2)); + ASSERT_FLOAT_EQ(214, YGNodeLayoutGetWidth(root_child1_child2)); + ASSERT_FLOAT_EQ(107, YGNodeLayoutGetHeight(root_child1_child2)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child2)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child2)); + ASSERT_FLOAT_EQ(213, YGNodeLayoutGetWidth(root_child2)); + ASSERT_FLOAT_EQ(320, YGNodeLayoutGetHeight(root_child2)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGSizeOverflowTest.cpp b/Sources/yoga-tests/YGSizeOverflowTest.cpp new file mode 100644 index 00000000..3fbcb300 --- /dev/null +++ b/Sources/yoga-tests/YGSizeOverflowTest.cpp @@ -0,0 +1,173 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// @Generated by gentest/gentest.rb from gentest/fixtures/YGSizeOverflowTest.html + +#include +#include + +TEST(YogaTest, nested_overflowing_child) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 200); + YGNodeStyleSetHeight(root_child0_child0, 200); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(-100, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, nested_overflowing_child_in_constraint_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeStyleSetHeight(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 200); + YGNodeStyleSetHeight(root_child0_child0, 200); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(-100, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} + +TEST(YogaTest, parent_wrap_child_size_overflowing_parent) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0, 100); + YGNodeInsertChild(root, root_child0, 0); + + const YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root_child0_child0, 100); + YGNodeStyleSetHeight(root_child0_child0, 200); + YGNodeInsertChild(root_child0, root_child0_child0, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0)); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0)); + ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(root_child0_child0)); + ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0)); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/Sources/yoga-tests/YGStyleTest.cpp b/Sources/yoga-tests/YGStyleTest.cpp new file mode 100644 index 00000000..5de45e45 --- /dev/null +++ b/Sources/yoga-tests/YGStyleTest.cpp @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include +#include + +TEST(YogaTest, copy_style_same) { + const YGNodeRef node0 = YGNodeNew(); + const YGNodeRef node1 = YGNodeNew(); + ASSERT_FALSE(node0->isDirty()); + + YGNodeCopyStyle(node0, node1); + ASSERT_FALSE(node0->isDirty()); + + YGNodeFree(node0); + YGNodeFree(node1); +} + +TEST(YogaTest, copy_style_modified) { + const YGNodeRef node0 = YGNodeNew(); + ASSERT_FALSE(node0->isDirty()); + ASSERT_EQ(YGFlexDirectionColumn, YGNodeStyleGetFlexDirection(node0)); + ASSERT_FALSE(YGNodeStyleGetMaxHeight(node0).unit != YGUnitUndefined); + + const YGNodeRef node1 = YGNodeNew(); + YGNodeStyleSetFlexDirection(node1, YGFlexDirectionRow); + YGNodeStyleSetMaxHeight(node1, 10); + + YGNodeCopyStyle(node0, node1); + ASSERT_TRUE(node0->isDirty()); + ASSERT_EQ(YGFlexDirectionRow, YGNodeStyleGetFlexDirection(node0)); + ASSERT_FLOAT_EQ(10, YGNodeStyleGetMaxHeight(node0).value); + + YGNodeFree(node0); + YGNodeFree(node1); +} + +TEST(YogaTest, copy_style_modified_same) { + const YGNodeRef node0 = YGNodeNew(); + YGNodeStyleSetFlexDirection(node0, YGFlexDirectionRow); + YGNodeStyleSetMaxHeight(node0, 10); + YGNodeCalculateLayout(node0, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FALSE(node0->isDirty()); + + const YGNodeRef node1 = YGNodeNew(); + YGNodeStyleSetFlexDirection(node1, YGFlexDirectionRow); + YGNodeStyleSetMaxHeight(node1, 10); + + YGNodeCopyStyle(node0, node1); + ASSERT_FALSE(node0->isDirty()); + + YGNodeFree(node0); + YGNodeFree(node1); +} diff --git a/Sources/yoga-tests/YGZeroOutLayoutRecursivlyTest.cpp b/Sources/yoga-tests/YGZeroOutLayoutRecursivlyTest.cpp new file mode 100644 index 00000000..92e7c8a0 --- /dev/null +++ b/Sources/yoga-tests/YGZeroOutLayoutRecursivlyTest.cpp @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +TEST(YogaTest, zero_out_layout) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 200); + YGNodeStyleSetHeight(root, 200); + + const YGNodeRef child = YGNodeNew(); + YGNodeInsertChild(root, child, 0); + YGNodeStyleSetWidth(child, 100); + YGNodeStyleSetHeight(child, 100); + YGNodeStyleSetMargin(child, YGEdgeTop, 10); + YGNodeStyleSetPadding(child, YGEdgeTop, 10); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetMargin(child, YGEdgeTop)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(child, YGEdgeTop)); + + YGNodeStyleSetDisplay(child, YGDisplayNone); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetMargin(child, YGEdgeTop)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(child, YGEdgeTop)); + + YGNodeFreeRecursive(root); +} diff --git a/Sources/yoga/Utils.cpp b/Sources/yoga/Utils.cpp new file mode 100644 index 00000000..110c7f0d --- /dev/null +++ b/Sources/yoga/Utils.cpp @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Utils.h" + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction) { + return YGFlexDirectionIsColumn(flexDirection) + ? YGResolveFlexDirection(YGFlexDirectionRow, direction) + : YGFlexDirectionColumn; +} + +bool YGValueEqual(const YGValue a, const YGValue b) { + if (a.unit != b.unit) { + return false; + } + + if (a.unit == YGUnitUndefined || + (std::isnan(a.value) && std::isnan(b.value))) { + return true; + } + + return fabs(a.value - b.value) < 0.0001f; +} diff --git a/Sources/yoga/Utils.h b/Sources/yoga/Utils.h new file mode 100644 index 00000000..db52acd6 --- /dev/null +++ b/Sources/yoga/Utils.h @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include "YGNode.h" +#include "Yoga-internal.h" + +// This struct is an helper model to hold the data for step 4 of flexbox +// algo, which is collecting the flex items in a line. +// +// - itemsOnLine: Number of items which can fit in a line considering the +// available Inner dimension, the flex items computed flexbasis and their +// margin. It may be different than the difference between start and end +// indicates because we skip over absolute-positioned items. +// +// - sizeConsumedOnCurrentLine: It is accumulation of the dimensions and margin +// of all the children on the current line. This will be used in order to either +// set the dimensions of the node if none already exist or to compute the +// remaining space left for the flexible children. +// +// - totalFlexGrowFactors: total flex grow factors of flex items which are to be +// layed in the current line +// +// - totalFlexShrinkFactors: total flex shrink factors of flex items which are +// to be layed in the current line +// +// - endOfLineIndex: Its the end index of the last flex item which was examined +// and it may or may not be part of the current line(as it may be absolutely +// positioned or inculding it may have caused to overshoot availableInnerDim) +// +// - relativeChildren: Maintain a vector of the child nodes that can shrink +// and/or grow. + +struct YGCollectFlexItemsRowValues { + uint32_t itemsOnLine; + float sizeConsumedOnCurrentLine; + float totalFlexGrowFactors; + float totalFlexShrinkScaledFactors; + float endOfLineIndex; + std::vector relativeChildren; + float remainingFreeSpace; + // The size of the mainDim for the row after considering size, padding, margin + // and border of flex items. This is used to calculate maxLineDim after going + // through all the rows to decide on the main axis size of parent. + float mainDim; + // The size of the crossDim for the row after considering size, padding, + // margin and border of flex items. Used for calculating containers crossSize. + float crossDim; +}; + +bool YGValueEqual(const YGValue a, const YGValue b); + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction); + +inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionRow || + flexDirection == YGFlexDirectionRowReverse; +} + +inline float YGResolveValue(const YGValue value, const float parentSize) { + switch (value.unit) { + case YGUnitUndefined: + case YGUnitAuto: + return YGUndefined; + case YGUnitPoint: + return value.value; + case YGUnitPercent: + return value.value * parentSize / 100.0f; + } + return YGUndefined; +} + +inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionColumn || + flexDirection == YGFlexDirectionColumnReverse; +} + +inline YGFlexDirection YGResolveFlexDirection( + const YGFlexDirection flexDirection, + const YGDirection direction) { + if (direction == YGDirectionRTL) { + if (flexDirection == YGFlexDirectionRow) { + return YGFlexDirectionRowReverse; + } else if (flexDirection == YGFlexDirectionRowReverse) { + return YGFlexDirectionRow; + } + } + + return flexDirection; +} + +static inline float YGResolveValueMargin( + const YGValue value, + const float parentSize) { + return value.unit == YGUnitAuto ? 0 : YGResolveValue(value, parentSize); +} diff --git a/Sources/yoga/YGEnums.c b/Sources/yoga/YGEnums.cpp similarity index 94% rename from Sources/yoga/YGEnums.c rename to Sources/yoga/YGEnums.cpp index f554c367..7f16628c 100644 --- a/Sources/yoga/YGEnums.c +++ b/Sources/yoga/YGEnums.cpp @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. */ #include "YGEnums.h" @@ -121,6 +119,8 @@ const char *YGJustifyToString(const YGJustify value){ return "space-between"; case YGJustifySpaceAround: return "space-around"; + case YGJustifySpaceEvenly: + return "space-evenly"; } return "unknown"; } @@ -224,4 +224,3 @@ const char *YGWrapToString(const YGWrap value){ } return "unknown"; } - diff --git a/Sources/yoga/YGEnums.h b/Sources/yoga/YGEnums.h index e12b9667..0976d78d 100644 --- a/Sources/yoga/YGEnums.h +++ b/Sources/yoga/YGEnums.h @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. */ #pragma once @@ -77,13 +75,14 @@ typedef YG_ENUM_BEGIN(YGFlexDirection) { } YG_ENUM_END(YGFlexDirection); WIN_EXPORT const char *YGFlexDirectionToString(const YGFlexDirection value); -#define YGJustifyCount 5 -typedef YG_ENUM_BEGIN(YGJustify) { - YGJustifyFlexStart, - YGJustifyCenter, - YGJustifyFlexEnd, - YGJustifySpaceBetween, - YGJustifySpaceAround, +#define YGJustifyCount 6 +typedef YG_ENUM_BEGIN(YGJustify){ + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, + YGJustifySpaceEvenly, } YG_ENUM_END(YGJustify); WIN_EXPORT const char *YGJustifyToString(const YGJustify value); diff --git a/Sources/yoga/YGLayout.cpp b/Sources/yoga/YGLayout.cpp new file mode 100644 index 00000000..66348fc2 --- /dev/null +++ b/Sources/yoga/YGLayout.cpp @@ -0,0 +1,66 @@ +/** + * 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 "YGLayout.h" + +const std::array kYGDefaultDimensionValues = { + {YGUndefined, YGUndefined}}; + +YGLayout::YGLayout() + : position(), + dimensions(kYGDefaultDimensionValues), + margin(), + border(), + padding(), + direction(YGDirectionInherit), + computedFlexBasisGeneration(0), + computedFlexBasis(YGUndefined), + hadOverflow(false), + generationCount(0), + lastParentDirection((YGDirection)-1), + nextCachedMeasurementsIndex(0), + cachedMeasurements(), + measuredDimensions(kYGDefaultDimensionValues), + cachedLayout(YGCachedMeasurement()), + didUseLegacyFlag(false), + doesLegacyStretchFlagAffectsLayout(false) {} + +bool YGLayout::operator==(YGLayout layout) const { + bool isEqual = position == layout.position && + dimensions == layout.dimensions && margin == layout.margin && + border == layout.border && padding == layout.padding && + direction == layout.direction && hadOverflow == layout.hadOverflow && + lastParentDirection == layout.lastParentDirection && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout; + + for (uint32_t i = 0; i < YG_MAX_CACHED_RESULT_COUNT && isEqual; ++i) { + isEqual = isEqual && cachedMeasurements[i] == layout.cachedMeasurements[i]; + } + + if (!YGFloatIsUndefined(computedFlexBasis) || + !YGFloatIsUndefined(layout.computedFlexBasis)) { + isEqual = isEqual && (computedFlexBasis == layout.computedFlexBasis); + } + if (!YGFloatIsUndefined(measuredDimensions[0]) || + !YGFloatIsUndefined(layout.measuredDimensions[0])) { + isEqual = + isEqual && (measuredDimensions[0] == layout.measuredDimensions[0]); + } + if (!YGFloatIsUndefined(measuredDimensions[1]) || + !YGFloatIsUndefined(layout.measuredDimensions[1])) { + isEqual = + isEqual && (measuredDimensions[1] == layout.measuredDimensions[1]); + } + + return isEqual; +} + +bool YGLayout::operator!=(YGLayout layout) const { + return !(*this == layout); +} diff --git a/Sources/yoga/YGLayout.h b/Sources/yoga/YGLayout.h new file mode 100644 index 00000000..125a6d0b --- /dev/null +++ b/Sources/yoga/YGLayout.h @@ -0,0 +1,42 @@ +/** + * 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. + */ +#pragma once +#include "Yoga-internal.h" + +struct YGLayout { + std::array position; + std::array dimensions; + std::array margin; + std::array border; + std::array padding; + YGDirection direction; + + uint32_t computedFlexBasisGeneration; + float computedFlexBasis; + bool hadOverflow; + + // Instead of recomputing the entire layout every single time, we + // cache some information to break early when nothing changed + uint32_t generationCount; + YGDirection lastParentDirection; + + uint32_t nextCachedMeasurementsIndex; + std::array + cachedMeasurements; + std::array measuredDimensions; + + YGCachedMeasurement cachedLayout; + bool didUseLegacyFlag; + bool doesLegacyStretchFlagAffectsLayout; + + YGLayout(); + + bool operator==(YGLayout layout) const; + bool operator!=(YGLayout layout) const; +}; diff --git a/Sources/yoga/YGMacros.h b/Sources/yoga/YGMacros.h index 628811a9..15ed81d3 100644 --- a/Sources/yoga/YGMacros.h +++ b/Sources/yoga/YGMacros.h @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. */ #pragma once @@ -31,10 +29,6 @@ #define WIN_STRUCT_REF(value) value #endif -#ifndef FB_ASSERTIONS_ENABLED -#define FB_ASSERTIONS_ENABLED 1 -#endif - #ifdef NS_ENUM // Cannot use NSInteger as NSInteger has a different size than int (which is the default type of a // enum). diff --git a/Sources/yoga/YGNode.cpp b/Sources/yoga/YGNode.cpp new file mode 100644 index 00000000..89be5bfe --- /dev/null +++ b/Sources/yoga/YGNode.cpp @@ -0,0 +1,743 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGNode.h" +#include +#include "Utils.h" + +void* YGNode::getContext() const { + return context_; +} + +YGPrintFunc YGNode::getPrintFunc() const { + return print_; +} + +bool YGNode::getHasNewLayout() const { + return hasNewLayout_; +} + +YGNodeType YGNode::getNodeType() const { + return nodeType_; +} + +YGMeasureFunc YGNode::getMeasure() const { + return measure_; +} + +YGBaselineFunc YGNode::getBaseline() const { + return baseline_; +} + +YGDirtiedFunc YGNode::getDirtied() const { + return dirtied_; +} + +YGStyle& YGNode::getStyle() { + return style_; +} + +YGLayout& YGNode::getLayout() { + return layout_; +} + +uint32_t YGNode::getLineIndex() const { + return lineIndex_; +} + +YGNodeRef YGNode::getParent() const { + return parent_; +} + +YGVector YGNode::getChildren() const { + return children_; +} + +uint32_t YGNode::getChildrenCount() const { + return static_cast(children_.size()); +} + +YGNodeRef YGNode::getChild(uint32_t index) const { + return children_.at(index); +} + +YGNodeRef YGNode::getNextChild() const { + return nextChild_; +} + +YGConfigRef YGNode::getConfig() const { + return config_; +} + +bool YGNode::isDirty() const { + return isDirty_; +} + +YGValue YGNode::getResolvedDimension(int index) { + return resolvedDimensions_[index]; +} + +std::array YGNode::getResolvedDimensions() const { + return resolvedDimensions_; +} + +float YGNode::getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) { + if (YGFlexDirectionIsRow(axis)) { + const YGValue* leadingPosition = + YGComputedEdgeValue(style_.position, YGEdgeStart, &YGValueUndefined); + if (leadingPosition->unit != YGUnitUndefined) { + return YGResolveValue(*leadingPosition, axisSize); + } + } + + const YGValue* leadingPosition = + YGComputedEdgeValue(style_.position, leading[axis], &YGValueUndefined); + + return leadingPosition->unit == YGUnitUndefined + ? 0.0f + : YGResolveValue(*leadingPosition, axisSize); +} + +float YGNode::getTrailingPosition( + const YGFlexDirection axis, + const float axisSize) { + if (YGFlexDirectionIsRow(axis)) { + const YGValue* trailingPosition = + YGComputedEdgeValue(style_.position, YGEdgeEnd, &YGValueUndefined); + if (trailingPosition->unit != YGUnitUndefined) { + return YGResolveValue(*trailingPosition, axisSize); + } + } + + const YGValue* trailingPosition = + YGComputedEdgeValue(style_.position, trailing[axis], &YGValueUndefined); + + return trailingPosition->unit == YGUnitUndefined + ? 0.0f + : YGResolveValue(*trailingPosition, axisSize); +} + +bool YGNode::isLeadingPositionDefined(const YGFlexDirection axis) { + return (YGFlexDirectionIsRow(axis) && + YGComputedEdgeValue(style_.position, YGEdgeStart, &YGValueUndefined) + ->unit != YGUnitUndefined) || + YGComputedEdgeValue(style_.position, leading[axis], &YGValueUndefined) + ->unit != YGUnitUndefined; +} + +bool YGNode::isTrailingPosDefined(const YGFlexDirection axis) { + return (YGFlexDirectionIsRow(axis) && + YGComputedEdgeValue(style_.position, YGEdgeEnd, &YGValueUndefined) + ->unit != YGUnitUndefined) || + YGComputedEdgeValue(style_.position, trailing[axis], &YGValueUndefined) + ->unit != YGUnitUndefined; +} + +float YGNode::getLeadingMargin( + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && + style_.margin[YGEdgeStart].unit != YGUnitUndefined) { + return YGResolveValueMargin(style_.margin[YGEdgeStart], widthSize); + } + + return YGResolveValueMargin( + *YGComputedEdgeValue(style_.margin, leading[axis], &YGValueZero), + widthSize); +} + +float YGNode::getTrailingMargin( + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && + style_.margin[YGEdgeEnd].unit != YGUnitUndefined) { + return YGResolveValueMargin(style_.margin[YGEdgeEnd], widthSize); + } + + return YGResolveValueMargin( + *YGComputedEdgeValue(style_.margin, trailing[axis], &YGValueZero), + widthSize); +} + +float YGNode::getMarginForAxis( + const YGFlexDirection axis, + const float widthSize) { + return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize); +} + +// Setters + +void YGNode::setContext(void* context) { + context_ = context; +} + +void YGNode::setPrintFunc(YGPrintFunc printFunc) { + print_ = printFunc; +} + +void YGNode::setHasNewLayout(bool hasNewLayout) { + hasNewLayout_ = hasNewLayout; +} + +void YGNode::setNodeType(YGNodeType nodeType) { + nodeType_ = nodeType; +} + +void YGNode::setStyleFlexDirection(YGFlexDirection direction) { + style_.flexDirection = direction; +} + +void YGNode::setStyleAlignContent(YGAlign alignContent) { + style_.alignContent = alignContent; +} + +void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { + if (measureFunc == nullptr) { + measure_ = nullptr; + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + nodeType_ = YGNodeTypeDefault; + } else { + YGAssertWithNode( + this, + children_.size() == 0, + "Cannot set measure function: Nodes with measure functions cannot have children."); + measure_ = measureFunc; + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(YGNodeTypeText); + } + + measure_ = measureFunc; +} + +void YGNode::setBaseLineFunc(YGBaselineFunc baseLineFunc) { + baseline_ = baseLineFunc; +} + +void YGNode::setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { + dirtied_ = dirtiedFunc; +} + +void YGNode::setStyle(YGStyle style) { + style_ = style; +} + +void YGNode::setLayout(YGLayout layout) { + layout_ = layout; +} + +void YGNode::setLineIndex(uint32_t lineIndex) { + lineIndex_ = lineIndex; +} + +void YGNode::setParent(YGNodeRef parent) { + parent_ = parent; +} + +void YGNode::setChildren(YGVector children) { + children_ = children; +} + +void YGNode::setNextChild(YGNodeRef nextChild) { + nextChild_ = nextChild; +} + +void YGNode::replaceChild(YGNodeRef child, uint32_t index) { + children_[index] = child; +} + +void YGNode::replaceChild(YGNodeRef oldChild, YGNodeRef newChild) { + std::replace(children_.begin(), children_.end(), oldChild, newChild); +} + +void YGNode::insertChild(YGNodeRef child, uint32_t index) { + children_.insert(children_.begin() + index, child); +} + +void YGNode::setConfig(YGConfigRef config) { + config_ = config; +} + +void YGNode::setDirty(bool isDirty) { + if (isDirty == isDirty_) { + return; + } + isDirty_ = isDirty; + if (isDirty && dirtied_) { + dirtied_(this); + } +} + +bool YGNode::removeChild(YGNodeRef child) { + std::vector::iterator p = + std::find(children_.begin(), children_.end(), child); + if (p != children_.end()) { + children_.erase(p); + return true; + } + return false; +} + +void YGNode::removeChild(uint32_t index) { + children_.erase(children_.begin() + index); +} + +void YGNode::setLayoutDirection(YGDirection direction) { + layout_.direction = direction; +} + +void YGNode::setLayoutMargin(float margin, int index) { + layout_.margin[index] = margin; +} + +void YGNode::setLayoutBorder(float border, int index) { + layout_.border[index] = border; +} + +void YGNode::setLayoutPadding(float padding, int index) { + layout_.padding[index] = padding; +} + +void YGNode::setLayoutLastParentDirection(YGDirection direction) { + layout_.lastParentDirection = direction; +} + +void YGNode::setLayoutComputedFlexBasis(float computedFlexBasis) { + layout_.computedFlexBasis = computedFlexBasis; +} + +void YGNode::setLayoutPosition(float position, int index) { + layout_.position[index] = position; +} + +void YGNode::setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration) { + layout_.computedFlexBasisGeneration = computedFlexBasisGeneration; +} + +void YGNode::setLayoutMeasuredDimension(float measuredDimension, int index) { + layout_.measuredDimensions[index] = measuredDimension; +} + +void YGNode::setLayoutHadOverflow(bool hadOverflow) { + layout_.hadOverflow = hadOverflow; +} + +void YGNode::setLayoutDimension(float dimension, int index) { + layout_.dimensions[index] = dimension; +} + +// If both left and right are defined, then use left. Otherwise return +// +left or -right depending on which is defined. +float YGNode::relativePosition( + const YGFlexDirection axis, + const float axisSize) { + return isLeadingPositionDefined(axis) ? getLeadingPosition(axis, axisSize) + : -getTrailingPosition(axis, axisSize); +} + +void YGNode::setPosition( + const YGDirection direction, + const float mainSize, + const float crossSize, + const float parentWidth) { + /* Root nodes should be always layouted as LTR, so we don't return negative + * values. */ + const YGDirection directionRespectingRoot = + parent_ != nullptr ? direction : YGDirectionLTR; + const YGFlexDirection mainAxis = + YGResolveFlexDirection(style_.flexDirection, directionRespectingRoot); + const YGFlexDirection crossAxis = + YGFlexDirectionCross(mainAxis, directionRespectingRoot); + + const float relativePositionMain = relativePosition(mainAxis, mainSize); + const float relativePositionCross = relativePosition(crossAxis, crossSize); + + setLayoutPosition( + getLeadingMargin(mainAxis, parentWidth) + relativePositionMain, + leading[mainAxis]); + setLayoutPosition( + getTrailingMargin(mainAxis, parentWidth) + relativePositionMain, + trailing[mainAxis]); + setLayoutPosition( + getLeadingMargin(crossAxis, parentWidth) + relativePositionCross, + leading[crossAxis]); + setLayoutPosition( + getTrailingMargin(crossAxis, parentWidth) + relativePositionCross, + trailing[crossAxis]); +} + +YGNode::YGNode() + : context_(nullptr), + print_(nullptr), + hasNewLayout_(true), + nodeType_(YGNodeTypeDefault), + measure_(nullptr), + baseline_(nullptr), + dirtied_(nullptr), + style_(YGStyle()), + layout_(YGLayout()), + lineIndex_(0), + parent_(nullptr), + children_(YGVector()), + nextChild_(nullptr), + config_(nullptr), + isDirty_(false), + resolvedDimensions_({{YGValueUndefined, YGValueUndefined}}) {} + +YGNode::YGNode(const YGNode& node) + : context_(node.context_), + print_(node.print_), + hasNewLayout_(node.hasNewLayout_), + nodeType_(node.nodeType_), + measure_(node.measure_), + baseline_(node.baseline_), + dirtied_(node.dirtied_), + style_(node.style_), + layout_(node.layout_), + lineIndex_(node.lineIndex_), + parent_(node.parent_), + children_(node.children_), + nextChild_(node.nextChild_), + config_(node.config_), + isDirty_(node.isDirty_), + resolvedDimensions_(node.resolvedDimensions_) {} + +YGNode::YGNode(const YGConfigRef newConfig) : YGNode() { + config_ = newConfig; +} + +YGNode::YGNode( + void* context, + YGPrintFunc print, + bool hasNewLayout, + YGNodeType nodeType, + YGMeasureFunc measure, + YGBaselineFunc baseline, + YGDirtiedFunc dirtied, + YGStyle style, + YGLayout layout, + uint32_t lineIndex, + YGNodeRef parent, + YGVector children, + YGNodeRef nextChild, + YGConfigRef config, + bool isDirty, + std::array resolvedDimensions) + : context_(context), + print_(print), + hasNewLayout_(hasNewLayout), + nodeType_(nodeType), + measure_(measure), + baseline_(baseline), + dirtied_(dirtied), + style_(style), + layout_(layout), + lineIndex_(lineIndex), + parent_(parent), + children_(children), + nextChild_(nextChild), + config_(config), + isDirty_(isDirty), + resolvedDimensions_(resolvedDimensions) {} + +YGNode& YGNode::operator=(const YGNode& node) { + if (&node == this) { + return *this; + } + + for (auto child : children_) { + delete child; + } + + context_ = node.getContext(); + print_ = node.getPrintFunc(); + hasNewLayout_ = node.getHasNewLayout(); + nodeType_ = node.getNodeType(); + measure_ = node.getMeasure(); + baseline_ = node.getBaseline(); + dirtied_ = node.getDirtied(); + style_ = node.style_; + layout_ = node.layout_; + lineIndex_ = node.getLineIndex(); + parent_ = node.getParent(); + children_ = node.getChildren(); + nextChild_ = node.getNextChild(); + config_ = node.getConfig(); + isDirty_ = node.isDirty(); + resolvedDimensions_ = node.getResolvedDimensions(); + + return *this; +} + +YGValue YGNode::marginLeadingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && + style_.margin[YGEdgeStart].unit != YGUnitUndefined) { + return style_.margin[YGEdgeStart]; + } else { + return style_.margin[leading[axis]]; + } +} + +YGValue YGNode::marginTrailingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && + style_.margin[YGEdgeEnd].unit != YGUnitUndefined) { + return style_.margin[YGEdgeEnd]; + } else { + return style_.margin[trailing[axis]]; + } +} + +YGValue YGNode::resolveFlexBasisPtr() const { + YGValue flexBasis = style_.flexBasis; + if (flexBasis.unit != YGUnitAuto && flexBasis.unit != YGUnitUndefined) { + return flexBasis; + } + if (!YGFloatIsUndefined(style_.flex) && style_.flex > 0.0f) { + return config_->useWebDefaults ? YGValueAuto : YGValueZero; + } + return YGValueAuto; +} + +void YGNode::resolveDimension() { + for (uint32_t dim = YGDimensionWidth; dim < YGDimensionCount; dim++) { + if (getStyle().maxDimensions[dim].unit != YGUnitUndefined && + YGValueEqual( + getStyle().maxDimensions[dim], style_.minDimensions[dim])) { + resolvedDimensions_[dim] = style_.maxDimensions[dim]; + } else { + resolvedDimensions_[dim] = style_.dimensions[dim]; + } + } +} + +YGDirection YGNode::resolveDirection(const YGDirection parentDirection) { + if (style_.direction == YGDirectionInherit) { + return parentDirection > YGDirectionInherit ? parentDirection + : YGDirectionLTR; + } else { + return style_.direction; + } +} + +void YGNode::clearChildren() { + children_.clear(); + children_.shrink_to_fit(); +} + +YGNode::~YGNode() { + // All the member variables are deallocated externally, so no need to + // deallocate here +} + +// Other Methods + +void YGNode::cloneChildrenIfNeeded() { + // YGNodeRemoveChild in yoga.cpp has a forked variant of this algorithm + // optimized for deletions. + + const uint32_t childCount = static_cast(children_.size()); + if (childCount == 0) { + // This is an empty set. Nothing to clone. + return; + } + + const YGNodeRef firstChild = children_.front(); + if (firstChild->getParent() == this) { + // If the first child has this node as its parent, we assume that it is + // already unique. We can do this because if we have it has a child, that + // means that its parent was at some point cloned which made that subtree + // immutable. We also assume that all its sibling are cloned as well. + return; + } + + const YGNodeClonedFunc cloneNodeCallback = config_->cloneNodeCallback; + for (uint32_t i = 0; i < childCount; ++i) { + const YGNodeRef oldChild = children_[i]; + const YGNodeRef newChild = YGNodeClone(oldChild); + replaceChild(newChild, i); + newChild->setParent(this); + if (cloneNodeCallback) { + cloneNodeCallback(oldChild, newChild, this, i); + } + } +} + +void YGNode::markDirtyAndPropogate() { + if (!isDirty_) { + setDirty(true); + setLayoutComputedFlexBasis(YGUndefined); + if (parent_) { + parent_->markDirtyAndPropogate(); + } + } +} + +void YGNode::markDirtyAndPropogateDownwards() { + isDirty_ = true; + for_each(children_.begin(), children_.end(), [](YGNodeRef childNode) { + childNode->markDirtyAndPropogateDownwards(); + }); +} + +float YGNode::resolveFlexGrow() { + // Root nodes flexGrow should always be 0 + if (parent_ == nullptr) { + return 0.0; + } + if (!YGFloatIsUndefined(style_.flexGrow)) { + return style_.flexGrow; + } + if (!YGFloatIsUndefined(style_.flex) && style_.flex > 0.0f) { + return style_.flex; + } + return kDefaultFlexGrow; +} + +float YGNode::resolveFlexShrink() { + if (parent_ == nullptr) { + return 0.0; + } + if (!YGFloatIsUndefined(style_.flexShrink)) { + return style_.flexShrink; + } + if (!config_->useWebDefaults && !YGFloatIsUndefined(style_.flex) && + style_.flex < 0.0f) { + return -style_.flex; + } + return config_->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink; +} + +bool YGNode::isNodeFlexible() { + return ( + (style_.positionType == YGPositionTypeRelative) && + (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +} + +float YGNode::getLeadingBorder(const YGFlexDirection axis) { + if (YGFlexDirectionIsRow(axis) && + style_.border[YGEdgeStart].unit != YGUnitUndefined && + style_.border[YGEdgeStart].value >= 0.0f) { + return style_.border[YGEdgeStart].value; + } + + return fmaxf( + YGComputedEdgeValue(style_.border, leading[axis], &YGValueZero)->value, + 0.0f); +} + +float YGNode::getTrailingBorder(const YGFlexDirection flexDirection) { + if (YGFlexDirectionIsRow(flexDirection) && + style_.border[YGEdgeEnd].unit != YGUnitUndefined && + style_.border[YGEdgeEnd].value >= 0.0f) { + return style_.border[YGEdgeEnd].value; + } + + return fmaxf( + YGComputedEdgeValue(style_.border, trailing[flexDirection], &YGValueZero) + ->value, + 0.0f); +} + +float YGNode::getLeadingPadding( + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && + style_.padding[YGEdgeStart].unit != YGUnitUndefined && + YGResolveValue(style_.padding[YGEdgeStart], widthSize) >= 0.0f) { + return YGResolveValue(style_.padding[YGEdgeStart], widthSize); + } + return fmaxf( + YGResolveValue( + *YGComputedEdgeValue(style_.padding, leading[axis], &YGValueZero), + widthSize), + 0.0f); +} + +float YGNode::getTrailingPadding( + const YGFlexDirection axis, + const float widthSize) { + if (YGFlexDirectionIsRow(axis) && + style_.padding[YGEdgeEnd].unit != YGUnitUndefined && + YGResolveValue(style_.padding[YGEdgeEnd], widthSize) >= 0.0f) { + return YGResolveValue(style_.padding[YGEdgeEnd], widthSize); + } + return fmaxf( + YGResolveValue( + *YGComputedEdgeValue(style_.padding, trailing[axis], &YGValueZero), + widthSize), + 0.0f); +} + +float YGNode::getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) { + return getLeadingPadding(axis, widthSize) + getLeadingBorder(axis); +} + +float YGNode::getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) { + return getTrailingPadding(axis, widthSize) + getTrailingBorder(axis); +} + +bool YGNode::didUseLegacyFlag() { + bool didUseLegacyFlag = layout_.didUseLegacyFlag; + if (didUseLegacyFlag) { + return true; + } + for (const auto& child : children_) { + if (child->layout_.didUseLegacyFlag) { + didUseLegacyFlag = true; + break; + } + } + return didUseLegacyFlag; +} + +void YGNode::setAndPropogateUseLegacyFlag(bool useLegacyFlag) { + config_->useLegacyStretchBehaviour = useLegacyFlag; + for_each(children_.begin(), children_.end(), [=](YGNodeRef childNode) { + childNode->getConfig()->useLegacyStretchBehaviour = useLegacyFlag; + }); +} + +void YGNode::setLayoutDoesLegacyFlagAffectsLayout( + bool doesLegacyFlagAffectsLayout) { + layout_.doesLegacyStretchFlagAffectsLayout = doesLegacyFlagAffectsLayout; +} + +void YGNode::setLayoutDidUseLegacyFlag(bool didUseLegacyFlag) { + layout_.didUseLegacyFlag = didUseLegacyFlag; +} + +bool YGNode::isLayoutTreeEqualToNode(const YGNode& node) const { + if (children_.size() != node.children_.size()) { + return false; + } + if (layout_ != node.layout_) { + return false; + } + if (children_.size() == 0) { + return true; + } + + bool isLayoutTreeEqual = true; + YGNodeRef otherNodeChildren = nullptr; + for (std::vector::size_type i = 0; i < children_.size(); ++i) { + otherNodeChildren = node.children_[i]; + isLayoutTreeEqual = + children_[i]->isLayoutTreeEqualToNode(*otherNodeChildren); + if (!isLayoutTreeEqual) { + return false; + } + } + return isLayoutTreeEqual; +} diff --git a/Sources/yoga/YGNode.h b/Sources/yoga/YGNode.h new file mode 100644 index 00000000..27a1ed68 --- /dev/null +++ b/Sources/yoga/YGNode.h @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include "YGLayout.h" +#include "YGStyle.h" +#include "Yoga-internal.h" + +struct YGNode { + private: + void* context_; + YGPrintFunc print_; + bool hasNewLayout_; + YGNodeType nodeType_; + YGMeasureFunc measure_; + YGBaselineFunc baseline_; + YGDirtiedFunc dirtied_; + YGStyle style_; + YGLayout layout_; + uint32_t lineIndex_; + YGNodeRef parent_; + YGVector children_; + YGNodeRef nextChild_; + YGConfigRef config_; + bool isDirty_; + std::array resolvedDimensions_; + + float relativePosition(const YGFlexDirection axis, const float axisSize); + + public: + YGNode(); + ~YGNode(); + explicit YGNode(const YGConfigRef newConfig); + YGNode(const YGNode& node); + YGNode& operator=(const YGNode& node); + YGNode( + void* context, + YGPrintFunc print, + bool hasNewLayout, + YGNodeType nodeType, + YGMeasureFunc measure, + YGBaselineFunc baseline, + YGDirtiedFunc dirtied, + YGStyle style, + YGLayout layout, + uint32_t lineIndex, + YGNodeRef parent, + YGVector children, + YGNodeRef nextChild, + YGConfigRef config, + bool isDirty, + std::array resolvedDimensions); + + // Getters + void* getContext() const; + YGPrintFunc getPrintFunc() const; + bool getHasNewLayout() const; + YGNodeType getNodeType() const; + YGMeasureFunc getMeasure() const; + YGBaselineFunc getBaseline() const; + YGDirtiedFunc getDirtied() const; + // For Performance reasons passing as reference. + YGStyle& getStyle(); + // For Performance reasons passing as reference. + YGLayout& getLayout(); + uint32_t getLineIndex() const; + YGNodeRef getParent() const; + YGVector getChildren() const; + uint32_t getChildrenCount() const; + YGNodeRef getChild(uint32_t index) const; + YGNodeRef getNextChild() const; + YGConfigRef getConfig() const; + bool isDirty() const; + std::array getResolvedDimensions() const; + YGValue getResolvedDimension(int index); + + // Methods related to positions, margin, padding and border + float getLeadingPosition(const YGFlexDirection axis, const float axisSize); + bool isLeadingPositionDefined(const YGFlexDirection axis); + bool isTrailingPosDefined(const YGFlexDirection axis); + float getTrailingPosition(const YGFlexDirection axis, const float axisSize); + float getLeadingMargin(const YGFlexDirection axis, const float widthSize); + float getTrailingMargin(const YGFlexDirection axis, const float widthSize); + float getLeadingBorder(const YGFlexDirection flexDirection); + float getTrailingBorder(const YGFlexDirection flexDirection); + float getLeadingPadding(const YGFlexDirection axis, const float widthSize); + float getTrailingPadding(const YGFlexDirection axis, const float widthSize); + float getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize); + float getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize); + float getMarginForAxis(const YGFlexDirection axis, const float widthSize); + // Setters + + void setContext(void* context); + void setPrintFunc(YGPrintFunc printFunc); + void setHasNewLayout(bool hasNewLayout); + void setNodeType(YGNodeType nodeTye); + void setMeasureFunc(YGMeasureFunc measureFunc); + void setBaseLineFunc(YGBaselineFunc baseLineFunc); + void setDirtiedFunc(YGDirtiedFunc dirtiedFunc); + void setStyle(YGStyle style); + void setStyleFlexDirection(YGFlexDirection direction); + void setStyleAlignContent(YGAlign alignContent); + void setLayout(YGLayout layout); + void setLineIndex(uint32_t lineIndex); + void setParent(YGNodeRef parent); + void setChildren(YGVector children); + void setNextChild(YGNodeRef nextChild); + void setConfig(YGConfigRef config); + void setDirty(bool isDirty); + void setLayoutLastParentDirection(YGDirection direction); + void setLayoutComputedFlexBasis(float computedFlexBasis); + void setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration); + void setLayoutMeasuredDimension(float measuredDimension, int index); + void setLayoutHadOverflow(bool hadOverflow); + void setLayoutDimension(float dimension, int index); + void setLayoutDirection(YGDirection direction); + void setLayoutMargin(float margin, int index); + void setLayoutBorder(float border, int index); + void setLayoutPadding(float padding, int index); + void setLayoutPosition(float position, int index); + void setPosition( + const YGDirection direction, + const float mainSize, + const float crossSize, + const float parentWidth); + void setAndPropogateUseLegacyFlag(bool useLegacyFlag); + void setLayoutDoesLegacyFlagAffectsLayout(bool doesLegacyFlagAffectsLayout); + void setLayoutDidUseLegacyFlag(bool didUseLegacyFlag); + void markDirtyAndPropogateDownwards(); + + // Other methods + YGValue marginLeadingValue(const YGFlexDirection axis) const; + YGValue marginTrailingValue(const YGFlexDirection axis) const; + YGValue resolveFlexBasisPtr() const; + void resolveDimension(); + YGDirection resolveDirection(const YGDirection parentDirection); + void clearChildren(); + /// Replaces the occurrences of oldChild with newChild + void replaceChild(YGNodeRef oldChild, YGNodeRef newChild); + void replaceChild(YGNodeRef child, uint32_t index); + void insertChild(YGNodeRef child, uint32_t index); + /// Removes the first occurrence of child + bool removeChild(YGNodeRef child); + void removeChild(uint32_t index); + + void cloneChildrenIfNeeded(); + void markDirtyAndPropogate(); + float resolveFlexGrow(); + float resolveFlexShrink(); + bool isNodeFlexible(); + bool didUseLegacyFlag(); + bool isLayoutTreeEqualToNode(const YGNode& node) const; +}; diff --git a/Sources/yoga/YGNodeList.c b/Sources/yoga/YGNodeList.c deleted file mode 100644 index 216085cc..00000000 --- a/Sources/yoga/YGNodeList.c +++ /dev/null @@ -1,104 +0,0 @@ -/** - * 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 "YGNodeList.h" - -extern YGMalloc gYGMalloc; -extern YGRealloc gYGRealloc; -extern YGFree gYGFree; - -struct YGNodeList { - uint32_t capacity; - uint32_t count; - YGNodeRef *items; -}; - -YGNodeListRef YGNodeListNew(const uint32_t initialCapacity) { - const YGNodeListRef list = gYGMalloc(sizeof(struct YGNodeList)); - YGAssert(list != NULL, "Could not allocate memory for list"); - - list->capacity = initialCapacity; - list->count = 0; - list->items = gYGMalloc(sizeof(YGNodeRef) * list->capacity); - YGAssert(list->items != NULL, "Could not allocate memory for items"); - - return list; -} - -void YGNodeListFree(const YGNodeListRef list) { - if (list) { - gYGFree(list->items); - gYGFree(list); - } -} - -uint32_t YGNodeListCount(const YGNodeListRef list) { - if (list) { - return list->count; - } - return 0; -} - -void YGNodeListAdd(YGNodeListRef *listp, const YGNodeRef node) { - if (!*listp) { - *listp = YGNodeListNew(4); - } - YGNodeListInsert(listp, node, (*listp)->count); -} - -void YGNodeListInsert(YGNodeListRef *listp, const YGNodeRef node, const uint32_t index) { - if (!*listp) { - *listp = YGNodeListNew(4); - } - YGNodeListRef list = *listp; - - if (list->count == list->capacity) { - list->capacity *= 2; - list->items = gYGRealloc(list->items, sizeof(YGNodeRef) * list->capacity); - YGAssert(list->items != NULL, "Could not extend allocation for items"); - } - - for (uint32_t i = list->count; i > index; i--) { - list->items[i] = list->items[i - 1]; - } - - list->count++; - list->items[index] = node; -} - -YGNodeRef YGNodeListRemove(const YGNodeListRef list, const uint32_t index) { - const YGNodeRef removed = list->items[index]; - list->items[index] = NULL; - - for (uint32_t i = index; i < list->count - 1; i++) { - list->items[i] = list->items[i + 1]; - list->items[i + 1] = NULL; - } - - list->count--; - return removed; -} - -YGNodeRef YGNodeListDelete(const YGNodeListRef list, const YGNodeRef node) { - for (uint32_t i = 0; i < list->count; i++) { - if (list->items[i] == node) { - return YGNodeListRemove(list, i); - } - } - - return NULL; -} - -YGNodeRef YGNodeListGet(const YGNodeListRef list, const uint32_t index) { - if (YGNodeListCount(list) > 0) { - return list->items[index]; - } - - return NULL; -} diff --git a/Sources/yoga/YGNodeList.h b/Sources/yoga/YGNodeList.h deleted file mode 100644 index 41e272ab..00000000 --- a/Sources/yoga/YGNodeList.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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. - */ - -#pragma once - -#include -#include -#include -#include - -#include "YGMacros.h" -#include "Yoga.h" - -YG_EXTERN_C_BEGIN - -typedef struct YGNodeList *YGNodeListRef; - -YGNodeListRef YGNodeListNew(const uint32_t initialCapacity); -void YGNodeListFree(const YGNodeListRef list); -uint32_t YGNodeListCount(const YGNodeListRef list); -void YGNodeListAdd(YGNodeListRef *listp, const YGNodeRef node); -void YGNodeListInsert(YGNodeListRef *listp, const YGNodeRef node, const uint32_t index); -YGNodeRef YGNodeListRemove(const YGNodeListRef list, const uint32_t index); -YGNodeRef YGNodeListDelete(const YGNodeListRef list, const YGNodeRef node); -YGNodeRef YGNodeListGet(const YGNodeListRef list, const uint32_t index); - -YG_EXTERN_C_END diff --git a/Sources/yoga/YGNodePrint.cpp b/Sources/yoga/YGNodePrint.cpp new file mode 100644 index 00000000..395fe181 --- /dev/null +++ b/Sources/yoga/YGNodePrint.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGNodePrint.h" +#include +#include "YGEnums.h" +#include "YGNode.h" +#include "Yoga-internal.h" + +namespace facebook { +namespace yoga { +typedef std::string string; + +static void indent(string* base, uint32_t level) { + for (uint32_t i = 0; i < level; ++i) { + base->append(" "); + } +} + +static bool areFourValuesEqual(const std::array& four) { + return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) && + YGValueEqual(four[0], four[3]); +} + +static void appendFormatedString(string* str, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + va_list argsCopy; + va_copy(argsCopy, args); + std::vector buf(1 + vsnprintf(NULL, 0, fmt, args)); + va_end(args); + vsnprintf(buf.data(), buf.size(), fmt, argsCopy); + va_end(argsCopy); + string result = string(buf.begin(), buf.end() - 1); + str->append(result); +} + +static void +appendFloatIfNotUndefined(string* base, const string key, const float num) { + if (!YGFloatIsUndefined(num)) { + appendFormatedString(base, "%s: %g; ", key.c_str(), num); + } +} + +static void appendNumberIfNotUndefined( + string* base, + const string key, + const YGValue number) { + if (number.unit != YGUnitUndefined) { + if (number.unit == YGUnitAuto) { + base->append(key + ": auto; "); + } else { + string unit = number.unit == YGUnitPoint ? "px" : "%%"; + appendFormatedString( + base, "%s: %g%s; ", key.c_str(), number.value, unit.c_str()); + } + } +} + +static void +appendNumberIfNotAuto(string* base, const string& key, const YGValue number) { + if (number.unit != YGUnitAuto) { + appendNumberIfNotUndefined(base, key, number); + } +} + +static void +appendNumberIfNotZero(string* base, const string& str, const YGValue number) { + if (!YGFloatsEqual(number.value, 0)) { + appendNumberIfNotUndefined(base, str, number); + } +} + +static void appendEdges( + string* base, + const string& key, + const std::array& edges) { + if (areFourValuesEqual(edges)) { + appendNumberIfNotZero(base, key, edges[YGEdgeLeft]); + } else { + for (int edge = YGEdgeLeft; edge != YGEdgeAll; ++edge) { + string str = key + "-" + YGEdgeToString(static_cast(edge)); + appendNumberIfNotZero(base, str, edges[edge]); + } + } +} + +static void appendEdgeIfNotUndefined( + string* base, + const string& str, + const std::array& edges, + const YGEdge edge) { + appendNumberIfNotUndefined( + base, str, *YGComputedEdgeValue(edges, edge, &YGValueUndefined)); +} + +void YGNodeToString( + std::string* str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level) { + indent(str, level); + appendFormatedString(str, "
getPrintFunc() != nullptr) { + node->getPrintFunc()(node); + } + + if (options & YGPrintOptionsLayout) { + appendFormatedString(str, "layout=\""); + appendFormatedString( + str, "width: %g; ", node->getLayout().dimensions[YGDimensionWidth]); + appendFormatedString( + str, "height: %g; ", node->getLayout().dimensions[YGDimensionHeight]); + appendFormatedString( + str, "top: %g; ", node->getLayout().position[YGEdgeTop]); + appendFormatedString( + str, "left: %g;", node->getLayout().position[YGEdgeLeft]); + appendFormatedString(str, "\" "); + } + + if (options & YGPrintOptionsStyle) { + appendFormatedString(str, "style=\""); + if (node->getStyle().flexDirection != YGNode().getStyle().flexDirection) { + appendFormatedString( + str, + "flex-direction: %s; ", + YGFlexDirectionToString(node->getStyle().flexDirection)); + } + if (node->getStyle().justifyContent != YGNode().getStyle().justifyContent) { + appendFormatedString( + str, + "justify-content: %s; ", + YGJustifyToString(node->getStyle().justifyContent)); + } + if (node->getStyle().alignItems != YGNode().getStyle().alignItems) { + appendFormatedString( + str, + "align-items: %s; ", + YGAlignToString(node->getStyle().alignItems)); + } + if (node->getStyle().alignContent != YGNode().getStyle().alignContent) { + appendFormatedString( + str, + "align-content: %s; ", + YGAlignToString(node->getStyle().alignContent)); + } + if (node->getStyle().alignSelf != YGNode().getStyle().alignSelf) { + appendFormatedString( + str, "align-self: %s; ", YGAlignToString(node->getStyle().alignSelf)); + } + appendFloatIfNotUndefined(str, "flex-grow", node->getStyle().flexGrow); + appendFloatIfNotUndefined(str, "flex-shrink", node->getStyle().flexShrink); + appendNumberIfNotAuto(str, "flex-basis", node->getStyle().flexBasis); + appendFloatIfNotUndefined(str, "flex", node->getStyle().flex); + + if (node->getStyle().flexWrap != YGNode().getStyle().flexWrap) { + appendFormatedString( + str, "flexWrap: %s; ", YGWrapToString(node->getStyle().flexWrap)); + } + + if (node->getStyle().overflow != YGNode().getStyle().overflow) { + appendFormatedString( + str, "overflow: %s; ", YGOverflowToString(node->getStyle().overflow)); + } + + if (node->getStyle().display != YGNode().getStyle().display) { + appendFormatedString( + str, "display: %s; ", YGDisplayToString(node->getStyle().display)); + } + appendEdges(str, "margin", node->getStyle().margin); + appendEdges(str, "padding", node->getStyle().padding); + appendEdges(str, "border", node->getStyle().border); + + appendNumberIfNotAuto( + str, "width", node->getStyle().dimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "height", node->getStyle().dimensions[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "max-width", node->getStyle().maxDimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "max-height", node->getStyle().maxDimensions[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "min-width", node->getStyle().minDimensions[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "min-height", node->getStyle().minDimensions[YGDimensionHeight]); + + if (node->getStyle().positionType != YGNode().getStyle().positionType) { + appendFormatedString( + str, + "position: %s; ", + YGPositionTypeToString(node->getStyle().positionType)); + } + + appendEdgeIfNotUndefined( + str, "left", node->getStyle().position, YGEdgeLeft); + appendEdgeIfNotUndefined( + str, "right", node->getStyle().position, YGEdgeRight); + appendEdgeIfNotUndefined(str, "top", node->getStyle().position, YGEdgeTop); + appendEdgeIfNotUndefined( + str, "bottom", node->getStyle().position, YGEdgeBottom); + appendFormatedString(str, "\" "); + + if (node->getMeasure() != nullptr) { + appendFormatedString(str, "has-custom-measure=\"true\""); + } + } + appendFormatedString(str, ">"); + + const uint32_t childCount = static_cast(node->getChildren().size()); + if (options & YGPrintOptionsChildren && childCount > 0) { + for (uint32_t i = 0; i < childCount; i++) { + appendFormatedString(str, "\n"); + YGNodeToString(str, YGNodeGetChild(node, i), options, level + 1); + } + appendFormatedString(str, "\n"); + indent(str, level); + } + appendFormatedString(str, "
"); +} +} // namespace yoga +} // namespace facebook diff --git a/Sources/yoga/YGNodePrint.h b/Sources/yoga/YGNodePrint.h new file mode 100644 index 00000000..16259fcf --- /dev/null +++ b/Sources/yoga/YGNodePrint.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#pragma once +#include + +#include "Yoga.h" + +namespace facebook { +namespace yoga { + +void YGNodeToString( + std::string* str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level); + +} // namespace yoga +} // namespace facebook diff --git a/Sources/yoga/YGStyle.cpp b/Sources/yoga/YGStyle.cpp new file mode 100644 index 00000000..a73cb3ce --- /dev/null +++ b/Sources/yoga/YGStyle.cpp @@ -0,0 +1,99 @@ +/** + * 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 "YGStyle.h" + +const YGValue kYGValueUndefined = {YGUndefined, YGUnitUndefined}; + +const YGValue kYGValueAuto = {YGUndefined, YGUnitAuto}; + +const std::array kYGDefaultEdgeValuesUnit = { + {kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined, + kYGValueUndefined}}; + +const std::array kYGDefaultDimensionValuesAutoUnit = { + {kYGValueAuto, kYGValueAuto}}; + +const std::array kYGDefaultDimensionValuesUnit = { + {kYGValueUndefined, kYGValueUndefined}}; + +YGStyle::YGStyle() + : direction(YGDirectionInherit), + flexDirection(YGFlexDirectionColumn), + justifyContent(YGJustifyFlexStart), + alignContent(YGAlignFlexStart), + alignItems(YGAlignStretch), + alignSelf(YGAlignAuto), + positionType(YGPositionTypeRelative), + flexWrap(YGWrapNoWrap), + overflow(YGOverflowVisible), + display(YGDisplayFlex), + flex(YGUndefined), + flexGrow(YGUndefined), + flexShrink(YGUndefined), + flexBasis(kYGValueAuto), + margin(kYGDefaultEdgeValuesUnit), + position(kYGDefaultEdgeValuesUnit), + padding(kYGDefaultEdgeValuesUnit), + border(kYGDefaultEdgeValuesUnit), + dimensions(kYGDefaultDimensionValuesAutoUnit), + minDimensions(kYGDefaultDimensionValuesUnit), + maxDimensions(kYGDefaultDimensionValuesUnit), + aspectRatio(YGUndefined) {} + +// Yoga specific properties, not compatible with flexbox specification +bool YGStyle::operator==(const YGStyle& style) { + bool areNonFloatValuesEqual = direction == style.direction && + flexDirection == style.flexDirection && + justifyContent == style.justifyContent && + alignContent == style.alignContent && alignItems == style.alignItems && + alignSelf == style.alignSelf && positionType == style.positionType && + flexWrap == style.flexWrap && overflow == style.overflow && + display == style.display && YGValueEqual(flexBasis, style.flexBasis) && + YGValueArrayEqual(margin, style.margin) && + YGValueArrayEqual(position, style.position) && + YGValueArrayEqual(padding, style.padding) && + YGValueArrayEqual(border, style.border) && + YGValueArrayEqual(dimensions, style.dimensions) && + YGValueArrayEqual(minDimensions, style.minDimensions) && + YGValueArrayEqual(maxDimensions, style.maxDimensions); + + if (!(std::isnan(flex) && std::isnan(style.flex))) { + areNonFloatValuesEqual = areNonFloatValuesEqual && flex == style.flex; + } + + if (!(std::isnan(flexGrow) && std::isnan(style.flexGrow))) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && flexGrow == style.flexGrow; + } + + if (!(std::isnan(flexShrink) && std::isnan(style.flexShrink))) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && flexShrink == style.flexShrink; + } + + if (!(std::isnan(aspectRatio) && std::isnan(style.aspectRatio))) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && aspectRatio == style.aspectRatio; + } + + return areNonFloatValuesEqual; +} + +bool YGStyle::operator!=(YGStyle style) { + return !(*this == style); +} + +YGStyle::~YGStyle() {} diff --git a/Sources/yoga/YGStyle.h b/Sources/yoga/YGStyle.h new file mode 100644 index 00000000..a95b4a29 --- /dev/null +++ b/Sources/yoga/YGStyle.h @@ -0,0 +1,43 @@ +/** + * 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. + */ +#pragma once +#include "Yoga-internal.h" +#include "Yoga.h" + +struct YGStyle { + YGDirection direction; + YGFlexDirection flexDirection; + YGJustify justifyContent; + YGAlign alignContent; + YGAlign alignItems; + YGAlign alignSelf; + YGPositionType positionType; + YGWrap flexWrap; + YGOverflow overflow; + YGDisplay display; + float flex; + float flexGrow; + float flexShrink; + YGValue flexBasis; + std::array margin; + std::array position; + std::array padding; + std::array border; + std::array dimensions; + std::array minDimensions; + std::array maxDimensions; + float aspectRatio; + + YGStyle(); + // Yoga specific properties, not compatible with flexbox specification + bool operator==(const YGStyle& style); + + bool operator!=(YGStyle style); + ~YGStyle(); +}; diff --git a/Sources/yoga/Yoga-internal.h b/Sources/yoga/Yoga-internal.h new file mode 100644 index 00000000..21b14b23 --- /dev/null +++ b/Sources/yoga/Yoga-internal.h @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include +#include +#include "Yoga.h" + +using YGVector = std::vector; + +YG_EXTERN_C_BEGIN + +WIN_EXPORT float YGRoundValueToPixelGrid(const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); + +YG_EXTERN_C_END + +extern const std::array trailing; +extern const std::array leading; +extern bool YGValueEqual(const YGValue a, const YGValue b); +extern const YGValue YGValueUndefined; +extern const YGValue YGValueAuto; +extern const YGValue YGValueZero; + +template +bool YGValueArrayEqual( + const std::array val1, + const std::array val2) { + bool areEqual = true; + for (uint32_t i = 0; i < size && areEqual; ++i) { + areEqual = YGValueEqual(val1[i], val2[i]); + } + return areEqual; +} + +struct YGCachedMeasurement { + float availableWidth; + float availableHeight; + YGMeasureMode widthMeasureMode; + YGMeasureMode heightMeasureMode; + + float computedWidth; + float computedHeight; + + YGCachedMeasurement() + : availableWidth(0), + availableHeight(0), + widthMeasureMode((YGMeasureMode)-1), + heightMeasureMode((YGMeasureMode)-1), + computedWidth(-1), + computedHeight(-1) {} + + bool operator==(YGCachedMeasurement measurement) const { + bool isEqual = widthMeasureMode == measurement.widthMeasureMode && + heightMeasureMode == measurement.heightMeasureMode; + + if (!std::isnan(availableWidth) || + !std::isnan(measurement.availableWidth)) { + isEqual = isEqual && availableWidth == measurement.availableWidth; + } + if (!std::isnan(availableHeight) || + !std::isnan(measurement.availableHeight)) { + isEqual = isEqual && availableHeight == measurement.availableHeight; + } + if (!std::isnan(computedWidth) || !std::isnan(measurement.computedWidth)) { + isEqual = isEqual && computedWidth == measurement.computedWidth; + } + if (!std::isnan(computedHeight) || + !std::isnan(measurement.computedHeight)) { + isEqual = isEqual && computedHeight == measurement.computedHeight; + } + + return isEqual; + } +}; + +// This value was chosen based on empiracle data. Even the most complicated +// layouts should not require more than 16 entries to fit within the cache. +#define YG_MAX_CACHED_RESULT_COUNT 16 + +struct YGConfig { + bool experimentalFeatures[YGExperimentalFeatureCount + 1]; + bool useWebDefaults; + bool useLegacyStretchBehaviour; + bool shouldDiffLayoutWithoutLegacyStretchBehaviour; + float pointScaleFactor; + YGLogger logger; + YGNodeClonedFunc cloneNodeCallback; + void* context; +}; + +static const float kDefaultFlexGrow = 0.0f; +static const float kDefaultFlexShrink = 0.0f; +static const float kWebDefaultFlexShrink = 1.0f; + +extern bool YGFloatsEqual(const float a, const float b); +extern bool YGValueEqual(const YGValue a, const YGValue b); +extern const YGValue* YGComputedEdgeValue( + const std::array& edges, + const YGEdge edge, + const YGValue* const defaultValue); diff --git a/Sources/yoga/Yoga.c b/Sources/yoga/Yoga.c deleted file mode 100644 index 398d3774..00000000 --- a/Sources/yoga/Yoga.c +++ /dev/null @@ -1,3673 +0,0 @@ -/** - * 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 "YGNodeList.h" -#include "Yoga.h" - -#ifdef _MSC_VER -#include -#ifndef isnan -#define isnan _isnan -#endif - -#ifndef __cplusplus -#define inline __inline -#endif - -/* define fmaxf if < VC12 */ -#if _MSC_VER < 1800 -__forceinline const float fmaxf(const float a, const float b) { - return (a > b) ? a : b; -} -#endif -#endif - -typedef struct YGCachedMeasurement { - float availableWidth; - float availableHeight; - YGMeasureMode widthMeasureMode; - YGMeasureMode heightMeasureMode; - - float computedWidth; - float computedHeight; -} YGCachedMeasurement; - -// This value was chosen based on empiracle data. Even the most complicated -// layouts should not require more than 16 entries to fit within the cache. -#define YG_MAX_CACHED_RESULT_COUNT 16 - -typedef struct YGLayout { - float position[4]; - float dimensions[2]; - float margin[6]; - float border[6]; - float padding[6]; - YGDirection direction; - - uint32_t computedFlexBasisGeneration; - float computedFlexBasis; - bool hadOverflow; - - // Instead of recomputing the entire layout every single time, we - // cache some information to break early when nothing changed - uint32_t generationCount; - YGDirection lastParentDirection; - - uint32_t nextCachedMeasurementsIndex; - YGCachedMeasurement cachedMeasurements[YG_MAX_CACHED_RESULT_COUNT]; - float measuredDimensions[2]; - - YGCachedMeasurement cachedLayout; -} YGLayout; - -typedef struct YGStyle { - YGDirection direction; - YGFlexDirection flexDirection; - YGJustify justifyContent; - YGAlign alignContent; - YGAlign alignItems; - YGAlign alignSelf; - YGPositionType positionType; - YGWrap flexWrap; - YGOverflow overflow; - YGDisplay display; - float flex; - float flexGrow; - float flexShrink; - YGValue flexBasis; - YGValue margin[YGEdgeCount]; - YGValue position[YGEdgeCount]; - YGValue padding[YGEdgeCount]; - YGValue border[YGEdgeCount]; - YGValue dimensions[2]; - YGValue minDimensions[2]; - YGValue maxDimensions[2]; - - // Yoga specific properties, not compatible with flexbox specification - float aspectRatio; -} YGStyle; - -typedef struct YGConfig { - bool experimentalFeatures[YGExperimentalFeatureCount + 1]; - bool useWebDefaults; - bool useLegacyStretchBehaviour; - float pointScaleFactor; - YGLogger logger; - void *context; -} YGConfig; - -typedef struct YGNode { - YGStyle style; - YGLayout layout; - uint32_t lineIndex; - - YGNodeRef parent; - YGNodeListRef children; - - struct YGNode *nextChild; - - YGMeasureFunc measure; - YGBaselineFunc baseline; - YGPrintFunc print; - YGConfigRef config; - void *context; - - bool isDirty; - bool hasNewLayout; - YGNodeType nodeType; - - YGValue const *resolvedDimensions[2]; -} YGNode; - -#define YG_UNDEFINED_VALUES \ - { .value = YGUndefined, .unit = YGUnitUndefined } - -#define YG_AUTO_VALUES \ - { .value = YGUndefined, .unit = YGUnitAuto } - -#define YG_DEFAULT_EDGE_VALUES_UNIT \ - { \ - [YGEdgeLeft] = YG_UNDEFINED_VALUES, [YGEdgeTop] = YG_UNDEFINED_VALUES, \ - [YGEdgeRight] = YG_UNDEFINED_VALUES, [YGEdgeBottom] = YG_UNDEFINED_VALUES, \ - [YGEdgeStart] = YG_UNDEFINED_VALUES, [YGEdgeEnd] = YG_UNDEFINED_VALUES, \ - [YGEdgeHorizontal] = YG_UNDEFINED_VALUES, [YGEdgeVertical] = YG_UNDEFINED_VALUES, \ - [YGEdgeAll] = YG_UNDEFINED_VALUES, \ - } - -#define YG_DEFAULT_DIMENSION_VALUES \ - { [YGDimensionWidth] = YGUndefined, [YGDimensionHeight] = YGUndefined, } - -#define YG_DEFAULT_DIMENSION_VALUES_UNIT \ - { [YGDimensionWidth] = YG_UNDEFINED_VALUES, [YGDimensionHeight] = YG_UNDEFINED_VALUES, } - -#define YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT \ - { [YGDimensionWidth] = YG_AUTO_VALUES, [YGDimensionHeight] = YG_AUTO_VALUES, } - -static const float kDefaultFlexGrow = 0.0f; -static const float kDefaultFlexShrink = 0.0f; -static const float kWebDefaultFlexShrink = 1.0f; - -static YGNode gYGNodeDefaults = { - .parent = NULL, - .children = NULL, - .hasNewLayout = true, - .isDirty = false, - .nodeType = YGNodeTypeDefault, - .resolvedDimensions = {[YGDimensionWidth] = &YGValueUndefined, - [YGDimensionHeight] = &YGValueUndefined}, - - .style = - { - .flex = YGUndefined, - .flexGrow = YGUndefined, - .flexShrink = YGUndefined, - .flexBasis = YG_AUTO_VALUES, - .justifyContent = YGJustifyFlexStart, - .alignItems = YGAlignStretch, - .alignContent = YGAlignFlexStart, - .direction = YGDirectionInherit, - .flexDirection = YGFlexDirectionColumn, - .overflow = YGOverflowVisible, - .display = YGDisplayFlex, - .dimensions = YG_DEFAULT_DIMENSION_VALUES_AUTO_UNIT, - .minDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT, - .maxDimensions = YG_DEFAULT_DIMENSION_VALUES_UNIT, - .position = YG_DEFAULT_EDGE_VALUES_UNIT, - .margin = YG_DEFAULT_EDGE_VALUES_UNIT, - .padding = YG_DEFAULT_EDGE_VALUES_UNIT, - .border = YG_DEFAULT_EDGE_VALUES_UNIT, - .aspectRatio = YGUndefined, - }, - - .layout = - { - .dimensions = YG_DEFAULT_DIMENSION_VALUES, - .lastParentDirection = (YGDirection) -1, - .nextCachedMeasurementsIndex = 0, - .computedFlexBasis = YGUndefined, - .hadOverflow = false, - .measuredDimensions = YG_DEFAULT_DIMENSION_VALUES, - - .cachedLayout = - { - .widthMeasureMode = (YGMeasureMode) -1, - .heightMeasureMode = (YGMeasureMode) -1, - .computedWidth = -1, - .computedHeight = -1, - }, - }, -}; - -#ifdef ANDROID -static int YGAndroidLog(const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args); -#else -static int YGDefaultLog(const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args); -#endif - -static YGConfig gYGConfigDefaults = { - .experimentalFeatures = - { - [YGExperimentalFeatureWebFlexBasis] = false, - }, - .useWebDefaults = false, - .pointScaleFactor = 1.0f, -#ifdef ANDROID - .logger = &YGAndroidLog, -#else - .logger = &YGDefaultLog, -#endif - .context = NULL, -}; - -static void YGNodeMarkDirtyInternal(const YGNodeRef node); - -YGMalloc gYGMalloc = &malloc; -YGCalloc gYGCalloc = &calloc; -YGRealloc gYGRealloc = &realloc; -YGFree gYGFree = &free; - -static YGValue YGValueZero = {.value = 0, .unit = YGUnitPoint}; - -#ifdef ANDROID -#include -static int YGAndroidLog(const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args) { - int androidLevel = YGLogLevelDebug; - switch (level) { - case YGLogLevelFatal: - androidLevel = ANDROID_LOG_FATAL; - break; - case YGLogLevelError: - androidLevel = ANDROID_LOG_ERROR; - break; - case YGLogLevelWarn: - androidLevel = ANDROID_LOG_WARN; - break; - case YGLogLevelInfo: - androidLevel = ANDROID_LOG_INFO; - break; - case YGLogLevelDebug: - androidLevel = ANDROID_LOG_DEBUG; - break; - case YGLogLevelVerbose: - androidLevel = ANDROID_LOG_VERBOSE; - break; - } - const int result = __android_log_vprint(androidLevel, "yoga", format, args); - return result; -} -#else -static int YGDefaultLog(const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args) { - switch (level) { - case YGLogLevelError: - case YGLogLevelFatal: - return vfprintf(stderr, format, args); - case YGLogLevelWarn: - case YGLogLevelInfo: - case YGLogLevelDebug: - case YGLogLevelVerbose: - default: - return vprintf(format, args); - } -} -#endif - -static inline const YGValue *YGComputedEdgeValue(const YGValue edges[YGEdgeCount], - const YGEdge edge, - const YGValue *const defaultValue) { - if (edges[edge].unit != YGUnitUndefined) { - return &edges[edge]; - } - - if ((edge == YGEdgeTop || edge == YGEdgeBottom) && - edges[YGEdgeVertical].unit != YGUnitUndefined) { - return &edges[YGEdgeVertical]; - } - - if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) && - edges[YGEdgeHorizontal].unit != YGUnitUndefined) { - return &edges[YGEdgeHorizontal]; - } - - if (edges[YGEdgeAll].unit != YGUnitUndefined) { - return &edges[YGEdgeAll]; - } - - if (edge == YGEdgeStart || edge == YGEdgeEnd) { - return &YGValueUndefined; - } - - return defaultValue; -} - -static inline float YGResolveValue(const YGValue *const value, const float parentSize) { - switch (value->unit) { - case YGUnitUndefined: - case YGUnitAuto: - return YGUndefined; - case YGUnitPoint: - return value->value; - case YGUnitPercent: - return value->value * parentSize / 100.0f; - } - return YGUndefined; -} - -static inline float YGResolveValueMargin(const YGValue *const value, const float parentSize) { - return value->unit == YGUnitAuto ? 0 : YGResolveValue(value, parentSize); -} - -int32_t gNodeInstanceCount = 0; -int32_t gConfigInstanceCount = 0; - -WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { - const YGNodeRef node = gYGMalloc(sizeof(YGNode)); - YGAssertWithConfig(config, node != NULL, "Could not allocate memory for node"); - gNodeInstanceCount++; - - memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); - if (config->useWebDefaults) { - node->style.flexDirection = YGFlexDirectionRow; - node->style.alignContent = YGAlignStretch; - } - node->config = config; - return node; -} - -YGNodeRef YGNodeNew(void) { - return YGNodeNewWithConfig(&gYGConfigDefaults); -} - -void YGNodeFree(const YGNodeRef node) { - if (node->parent) { - YGNodeListDelete(node->parent->children, node); - node->parent = NULL; - } - - const uint32_t childCount = YGNodeGetChildCount(node); - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeGetChild(node, i); - child->parent = NULL; - } - - YGNodeListFree(node->children); - gYGFree(node); - gNodeInstanceCount--; -} - -void YGNodeFreeRecursive(const YGNodeRef root) { - while (YGNodeGetChildCount(root) > 0) { - const YGNodeRef child = YGNodeGetChild(root, 0); - YGNodeRemoveChild(root, child); - YGNodeFreeRecursive(child); - } - YGNodeFree(root); -} - -void YGNodeReset(const YGNodeRef node) { - YGAssertWithNode(node, - YGNodeGetChildCount(node) == 0, - "Cannot reset a node which still has children attached"); - YGAssertWithNode(node, node->parent == NULL, "Cannot reset a node still attached to a parent"); - - YGNodeListFree(node->children); - - const YGConfigRef config = node->config; - memcpy(node, &gYGNodeDefaults, sizeof(YGNode)); - if (config->useWebDefaults) { - node->style.flexDirection = YGFlexDirectionRow; - node->style.alignContent = YGAlignStretch; - } - node->config = config; -} - -int32_t YGNodeGetInstanceCount(void) { - return gNodeInstanceCount; -} - -int32_t YGConfigGetInstanceCount(void) { - return gConfigInstanceCount; -} - -// Export only for C# -YGConfigRef YGConfigGetDefault() { - return &gYGConfigDefaults; -} - -YGConfigRef YGConfigNew(void) { - const YGConfigRef config = gYGMalloc(sizeof(YGConfig)); - YGAssert(config != NULL, "Could not allocate memory for config"); - - gConfigInstanceCount++; - memcpy(config, &gYGConfigDefaults, sizeof(YGConfig)); - return config; -} - -void YGConfigFree(const YGConfigRef config) { - gYGFree(config); - gConfigInstanceCount--; -} - -void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) { - memcpy(dest, src, sizeof(YGConfig)); -} - -static void YGNodeMarkDirtyInternal(const YGNodeRef node) { - if (!node->isDirty) { - node->isDirty = true; - node->layout.computedFlexBasis = YGUndefined; - if (node->parent) { - YGNodeMarkDirtyInternal(node->parent); - } - } -} - -void YGNodeSetMeasureFunc(const YGNodeRef node, YGMeasureFunc measureFunc) { - if (measureFunc == NULL) { - node->measure = NULL; - // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho - node->nodeType = YGNodeTypeDefault; - } else { - YGAssertWithNode( - node, - YGNodeGetChildCount(node) == 0, - "Cannot set measure function: Nodes with measure functions cannot have children."); - node->measure = measureFunc; - // TODO: t18095186 Move nodeType to opt-in function and mark appropriate places in Litho - node->nodeType = YGNodeTypeText; - } -} - -YGMeasureFunc YGNodeGetMeasureFunc(const YGNodeRef node) { - return node->measure; -} - -void YGNodeSetBaselineFunc(const YGNodeRef node, YGBaselineFunc baselineFunc) { - node->baseline = baselineFunc; -} - -YGBaselineFunc YGNodeGetBaselineFunc(const YGNodeRef node) { - return node->baseline; -} - -void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) { - YGAssertWithNode(node, - child->parent == NULL, - "Child already has a parent, it must be removed first."); - YGAssertWithNode(node, - node->measure == NULL, - "Cannot add child: Nodes with measure functions cannot have children."); - - YGNodeListInsert(&node->children, child, index); - child->parent = node; - YGNodeMarkDirtyInternal(node); -} - -void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child) { - if (YGNodeListDelete(node->children, child) != NULL) { - child->layout = gYGNodeDefaults.layout; // layout is no longer valid - child->parent = NULL; - YGNodeMarkDirtyInternal(node); - } -} - -YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) { - return YGNodeListGet(node->children, index); -} - -YGNodeRef YGNodeGetParent(const YGNodeRef node) { - return node->parent; -} - -inline uint32_t YGNodeGetChildCount(const YGNodeRef node) { - return YGNodeListCount(node->children); -} - -void YGNodeMarkDirty(const YGNodeRef node) { - YGAssertWithNode(node, - node->measure != NULL, - "Only leaf nodes with custom measure functions" - "should manually mark themselves as dirty"); - - YGNodeMarkDirtyInternal(node); -} - -bool YGNodeIsDirty(const YGNodeRef node) { - return node->isDirty; -} - -void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) { - if (memcmp(&dstNode->style, &srcNode->style, sizeof(YGStyle)) != 0) { - memcpy(&dstNode->style, &srcNode->style, sizeof(YGStyle)); - YGNodeMarkDirtyInternal(dstNode); - } -} - -static inline float YGResolveFlexGrow(const YGNodeRef node) { - // Root nodes flexGrow should always be 0 - if (node->parent == NULL) { - return 0.0; - } - if (!YGFloatIsUndefined(node->style.flexGrow)) { - return node->style.flexGrow; - } - if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) { - return node->style.flex; - } - return kDefaultFlexGrow; -} - -float YGNodeStyleGetFlexGrow(const YGNodeRef node) { - return YGFloatIsUndefined(node->style.flexGrow) ? kDefaultFlexGrow : node->style.flexGrow; -} - -float YGNodeStyleGetFlexShrink(const YGNodeRef node) { - return YGFloatIsUndefined(node->style.flexShrink) - ? (node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink) - : node->style.flexShrink; -} - -static inline float YGNodeResolveFlexShrink(const YGNodeRef node) { - // Root nodes flexShrink should always be 0 - if (node->parent == NULL) { - return 0.0; - } - if (!YGFloatIsUndefined(node->style.flexShrink)) { - return node->style.flexShrink; - } - if (!node->config->useWebDefaults && !YGFloatIsUndefined(node->style.flex) && - node->style.flex < 0.0f) { - return -node->style.flex; - } - return node->config->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink; -} - -static inline const YGValue *YGNodeResolveFlexBasisPtr(const YGNodeRef node) { - if (node->style.flexBasis.unit != YGUnitAuto && node->style.flexBasis.unit != YGUnitUndefined) { - return &node->style.flexBasis; - } - if (!YGFloatIsUndefined(node->style.flex) && node->style.flex > 0.0f) { - return node->config->useWebDefaults ? &YGValueAuto : &YGValueZero; - } - return &YGValueAuto; -} - -#define YG_NODE_PROPERTY_IMPL(type, name, paramName, instanceName) \ - void YGNodeSet##name(const YGNodeRef node, type paramName) { \ - node->instanceName = paramName; \ - } \ - \ - type YGNodeGet##name(const YGNodeRef node) { \ - return node->instanceName; \ - } - -#define YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \ - void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ - if (node->style.instanceName != paramName) { \ - node->style.instanceName = paramName; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } - -#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(type, name, paramName, instanceName) \ - void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ - if (node->style.instanceName.value != paramName || \ - node->style.instanceName.unit != YGUnitPoint) { \ - node->style.instanceName.value = paramName; \ - node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPoint; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - void YGNodeStyleSet##name##Percent(const YGNodeRef node, const type paramName) { \ - if (node->style.instanceName.value != paramName || \ - node->style.instanceName.unit != YGUnitPercent) { \ - node->style.instanceName.value = paramName; \ - node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } - -#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(type, name, paramName, instanceName) \ - void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ - if (node->style.instanceName.value != paramName || \ - node->style.instanceName.unit != YGUnitPoint) { \ - node->style.instanceName.value = paramName; \ - node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPoint; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - void YGNodeStyleSet##name##Percent(const YGNodeRef node, const type paramName) { \ - if (node->style.instanceName.value != paramName || \ - node->style.instanceName.unit != YGUnitPercent) { \ - node->style.instanceName.value = paramName; \ - node->style.instanceName.unit = YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \ - if (node->style.instanceName.unit != YGUnitAuto) { \ - node->style.instanceName.value = YGUndefined; \ - node->style.instanceName.unit = YGUnitAuto; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } - -#define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \ - YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \ - \ - type YGNodeStyleGet##name(const YGNodeRef node) { \ - return node->style.instanceName; \ - } - -#define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \ - YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(float, name, paramName, instanceName) \ - \ - type YGNodeStyleGet##name(const YGNodeRef node) { \ - return node->style.instanceName; \ - } - -#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(type, name, paramName, instanceName) \ - YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(float, name, paramName, instanceName) \ - \ - type YGNodeStyleGet##name(const YGNodeRef node) { \ - return node->style.instanceName; \ - } - -#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \ - void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \ - if (node->style.instanceName[edge].unit != YGUnitAuto) { \ - node->style.instanceName[edge].value = YGUndefined; \ - node->style.instanceName[edge].unit = YGUnitAuto; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } - -#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \ - void YGNodeStyleSet##name(const YGNodeRef node, const YGEdge edge, const float paramName) { \ - if (node->style.instanceName[edge].value != paramName || \ - node->style.instanceName[edge].unit != YGUnitPoint) { \ - node->style.instanceName[edge].value = paramName; \ - node->style.instanceName[edge].unit = \ - YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - void YGNodeStyleSet##name##Percent(const YGNodeRef node, \ - const YGEdge edge, \ - const float paramName) { \ - if (node->style.instanceName[edge].value != paramName || \ - node->style.instanceName[edge].unit != YGUnitPercent) { \ - node->style.instanceName[edge].value = paramName; \ - node->style.instanceName[edge].unit = \ - YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - WIN_STRUCT(type) YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ - return WIN_STRUCT_REF(node->style.instanceName[edge]); \ - } - -#define YG_NODE_STYLE_EDGE_PROPERTY_IMPL(type, name, paramName, instanceName) \ - void YGNodeStyleSet##name(const YGNodeRef node, const YGEdge edge, const float paramName) { \ - if (node->style.instanceName[edge].value != paramName || \ - node->style.instanceName[edge].unit != YGUnitPoint) { \ - node->style.instanceName[edge].value = paramName; \ - node->style.instanceName[edge].unit = \ - YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint; \ - YGNodeMarkDirtyInternal(node); \ - } \ - } \ - \ - float YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ - return node->style.instanceName[edge].value; \ - } - -#define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \ - type YGNodeLayoutGet##name(const YGNodeRef node) { \ - return node->layout.instanceName; \ - } - -#define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \ - type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \ - YGAssertWithNode(node, \ - edge < YGEdgeEnd, \ - "Cannot get layout properties of multi-edge shorthands"); \ - \ - if (edge == YGEdgeLeft) { \ - if (node->layout.direction == YGDirectionRTL) { \ - return node->layout.instanceName[YGEdgeEnd]; \ - } else { \ - return node->layout.instanceName[YGEdgeStart]; \ - } \ - } \ - \ - if (edge == YGEdgeRight) { \ - if (node->layout.direction == YGDirectionRTL) { \ - return node->layout.instanceName[YGEdgeStart]; \ - } else { \ - return node->layout.instanceName[YGEdgeEnd]; \ - } \ - } \ - \ - return node->layout.instanceName[edge]; \ - } - -YG_NODE_PROPERTY_IMPL(void *, Context, context, context); -YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print); -YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout); -YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType); - -YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction); -YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection); -YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent); -YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent); -YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems); -YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf); -YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType); -YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap); -YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow); -YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display); - -YG_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); -YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexGrow, flexGrow, flexGrow); -YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexShrink, flexShrink, flexShrink); -YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, FlexBasis, flexBasis, flexBasis); - -YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position); -YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin); -YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin); -YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding); -YG_NODE_STYLE_EDGE_PROPERTY_IMPL(float, Border, border, border); - -YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]); -YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]); -YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]); -YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]); -YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]); -YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]); - -// Yoga specific properties, not compatible with flexbox specification -YG_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); - -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]); -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]); -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]); -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]); -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]); -YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]); -YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction); -YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow); - -YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin); -YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border); -YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding); - -uint32_t gCurrentGenerationCount = 0; - -bool YGLayoutNodeInternal(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGDirection parentDirection, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight, - const bool performLayout, - const char *reason, - const YGConfigRef config); - -inline bool YGFloatIsUndefined(const float value) { - return isnan(value); -} - -static inline bool YGValueEqual(const YGValue a, const YGValue b) { - if (a.unit != b.unit) { - return false; - } - - if (a.unit == YGUnitUndefined) { - return true; - } - - return fabs(a.value - b.value) < 0.0001f; -} - -static inline void YGResolveDimensions(YGNodeRef node) { - for (YGDimension dim = YGDimensionWidth; dim <= YGDimensionHeight; dim++) { - if (node->style.maxDimensions[dim].unit != YGUnitUndefined && - YGValueEqual(node->style.maxDimensions[dim], node->style.minDimensions[dim])) { - node->resolvedDimensions[dim] = &node->style.maxDimensions[dim]; - } else { - node->resolvedDimensions[dim] = &node->style.dimensions[dim]; - } - } -} - -static inline bool YGFloatsEqual(const float a, const float b) { - if (YGFloatIsUndefined(a)) { - return YGFloatIsUndefined(b); - } - return fabs(a - b) < 0.0001f; -} - -static void YGIndent(const YGNodeRef node, const uint32_t n) { - for (uint32_t i = 0; i < n; i++) { - YGLog(node, YGLogLevelDebug, " "); - } -} - -static void YGPrintNumberIfNotUndefinedf(const YGNodeRef node, - const char *str, - const float number) { - if (!YGFloatIsUndefined(number)) { - YGLog(node, YGLogLevelDebug, "%s: %g; ", str, number); - } -} - -static void YGPrintNumberIfNotUndefined(const YGNodeRef node, - const char *str, - const YGValue *const number) { - if (number->unit != YGUnitUndefined) { - if (number->unit == YGUnitAuto) { - YGLog(node, YGLogLevelDebug, "%s: auto; ", str); - } else { - const char *unit = number->unit == YGUnitPoint ? "px" : "%"; - YGLog(node, YGLogLevelDebug, "%s: %g%s; ", str, number->value, unit); - } - } -} - -static void YGPrintNumberIfNotAuto(const YGNodeRef node, - const char *str, - const YGValue *const number) { - if (number->unit != YGUnitAuto) { - YGPrintNumberIfNotUndefined(node, str, number); - } -} - -static void YGPrintEdgeIfNotUndefined(const YGNodeRef node, - const char *str, - const YGValue *edges, - const YGEdge edge) { - YGPrintNumberIfNotUndefined(node, str, YGComputedEdgeValue(edges, edge, &YGValueUndefined)); -} - -static void YGPrintNumberIfNotZero(const YGNodeRef node, - const char *str, - const YGValue *const number) { - if (!YGFloatsEqual(number->value, 0)) { - YGPrintNumberIfNotUndefined(node, str, number); - } -} - -static bool YGFourValuesEqual(const YGValue four[4]) { - return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) && - YGValueEqual(four[0], four[3]); -} - -static void YGPrintEdges(const YGNodeRef node, const char *str, const YGValue *edges) { - if (YGFourValuesEqual(edges)) { - YGPrintNumberIfNotZero(node, str, &edges[YGEdgeLeft]); - } else { - for (YGEdge edge = YGEdgeLeft; edge < YGEdgeCount; edge++) { - char buf[30]; - snprintf(buf, sizeof(buf), "%s-%s", str, YGEdgeToString(edge)); - YGPrintNumberIfNotZero(node, buf, &edges[edge]); - } - } -} - -static void YGNodePrintInternal(const YGNodeRef node, - const YGPrintOptions options, - const uint32_t level) { - YGIndent(node, level); - YGLog(node, YGLogLevelDebug, "
print) { - node->print(node); - } - - if (options & YGPrintOptionsLayout) { - YGLog(node, YGLogLevelDebug, "layout=\""); - YGLog(node, YGLogLevelDebug, "width: %g; ", node->layout.dimensions[YGDimensionWidth]); - YGLog(node, YGLogLevelDebug, "height: %g; ", node->layout.dimensions[YGDimensionHeight]); - YGLog(node, YGLogLevelDebug, "top: %g; ", node->layout.position[YGEdgeTop]); - YGLog(node, YGLogLevelDebug, "left: %g;", node->layout.position[YGEdgeLeft]); - YGLog(node, YGLogLevelDebug, "\" "); - } - - if (options & YGPrintOptionsStyle) { - YGLog(node, YGLogLevelDebug, "style=\""); - if (node->style.flexDirection != gYGNodeDefaults.style.flexDirection) { - YGLog(node, - YGLogLevelDebug, - "flex-direction: %s; ", - YGFlexDirectionToString(node->style.flexDirection)); - } - if (node->style.justifyContent != gYGNodeDefaults.style.justifyContent) { - YGLog(node, - YGLogLevelDebug, - "justify-content: %s; ", - YGJustifyToString(node->style.justifyContent)); - } - if (node->style.alignItems != gYGNodeDefaults.style.alignItems) { - YGLog(node, YGLogLevelDebug, "align-items: %s; ", YGAlignToString(node->style.alignItems)); - } - if (node->style.alignContent != gYGNodeDefaults.style.alignContent) { - YGLog(node, YGLogLevelDebug, "align-content: %s; ", YGAlignToString(node->style.alignContent)); - } - if (node->style.alignSelf != gYGNodeDefaults.style.alignSelf) { - YGLog(node, YGLogLevelDebug, "align-self: %s; ", YGAlignToString(node->style.alignSelf)); - } - - YGPrintNumberIfNotUndefinedf(node, "flex-grow", node->style.flexGrow); - YGPrintNumberIfNotUndefinedf(node, "flex-shrink", node->style.flexShrink); - YGPrintNumberIfNotAuto(node, "flex-basis", &node->style.flexBasis); - YGPrintNumberIfNotUndefinedf(node, "flex", node->style.flex); - - if (node->style.flexWrap != gYGNodeDefaults.style.flexWrap) { - YGLog(node, YGLogLevelDebug, "flexWrap: %s; ", YGWrapToString(node->style.flexWrap)); - } - - if (node->style.overflow != gYGNodeDefaults.style.overflow) { - YGLog(node, YGLogLevelDebug, "overflow: %s; ", YGOverflowToString(node->style.overflow)); - } - - if (node->style.display != gYGNodeDefaults.style.display) { - YGLog(node, YGLogLevelDebug, "display: %s; ", YGDisplayToString(node->style.display)); - } - - YGPrintEdges(node, "margin", node->style.margin); - YGPrintEdges(node, "padding", node->style.padding); - YGPrintEdges(node, "border", node->style.border); - - YGPrintNumberIfNotAuto(node, "width", &node->style.dimensions[YGDimensionWidth]); - YGPrintNumberIfNotAuto(node, "height", &node->style.dimensions[YGDimensionHeight]); - YGPrintNumberIfNotAuto(node, "max-width", &node->style.maxDimensions[YGDimensionWidth]); - YGPrintNumberIfNotAuto(node, "max-height", &node->style.maxDimensions[YGDimensionHeight]); - YGPrintNumberIfNotAuto(node, "min-width", &node->style.minDimensions[YGDimensionWidth]); - YGPrintNumberIfNotAuto(node, "min-height", &node->style.minDimensions[YGDimensionHeight]); - - if (node->style.positionType != gYGNodeDefaults.style.positionType) { - YGLog(node, - YGLogLevelDebug, - "position: %s; ", - YGPositionTypeToString(node->style.positionType)); - } - - YGPrintEdgeIfNotUndefined(node, "left", node->style.position, YGEdgeLeft); - YGPrintEdgeIfNotUndefined(node, "right", node->style.position, YGEdgeRight); - YGPrintEdgeIfNotUndefined(node, "top", node->style.position, YGEdgeTop); - YGPrintEdgeIfNotUndefined(node, "bottom", node->style.position, YGEdgeBottom); - YGLog(node, YGLogLevelDebug, "\" "); - - if (node->measure != NULL) { - YGLog(node, YGLogLevelDebug, "has-custom-measure=\"true\""); - } - } - YGLog(node, YGLogLevelDebug, ">"); - - const uint32_t childCount = YGNodeListCount(node->children); - if (options & YGPrintOptionsChildren && childCount > 0) { - for (uint32_t i = 0; i < childCount; i++) { - YGLog(node, YGLogLevelDebug, "\n"); - YGNodePrintInternal(YGNodeGetChild(node, i), options, level + 1); - } - YGIndent(node, level); - YGLog(node, YGLogLevelDebug, "\n"); - } - YGLog(node, YGLogLevelDebug, "
"); -} - -void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) { - YGNodePrintInternal(node, options, 0); -} - -static const YGEdge leading[4] = { - [YGFlexDirectionColumn] = YGEdgeTop, - [YGFlexDirectionColumnReverse] = YGEdgeBottom, - [YGFlexDirectionRow] = YGEdgeLeft, - [YGFlexDirectionRowReverse] = YGEdgeRight, -}; -static const YGEdge trailing[4] = { - [YGFlexDirectionColumn] = YGEdgeBottom, - [YGFlexDirectionColumnReverse] = YGEdgeTop, - [YGFlexDirectionRow] = YGEdgeRight, - [YGFlexDirectionRowReverse] = YGEdgeLeft, -}; -static const YGEdge pos[4] = { - [YGFlexDirectionColumn] = YGEdgeTop, - [YGFlexDirectionColumnReverse] = YGEdgeBottom, - [YGFlexDirectionRow] = YGEdgeLeft, - [YGFlexDirectionRowReverse] = YGEdgeRight, -}; -static const YGDimension dim[4] = { - [YGFlexDirectionColumn] = YGDimensionHeight, - [YGFlexDirectionColumnReverse] = YGDimensionHeight, - [YGFlexDirectionRow] = YGDimensionWidth, - [YGFlexDirectionRowReverse] = YGDimensionWidth, -}; - -static inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { - return flexDirection == YGFlexDirectionRow || flexDirection == YGFlexDirectionRowReverse; -} - -static inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) { - return flexDirection == YGFlexDirectionColumn || flexDirection == YGFlexDirectionColumnReverse; -} - -static inline float YGNodeLeadingMargin(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) { - return YGResolveValueMargin(&node->style.margin[YGEdgeStart], widthSize); - } - - return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, leading[axis], &YGValueZero), - widthSize); -} - -static float YGNodeTrailingMargin(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) { - return YGResolveValueMargin(&node->style.margin[YGEdgeEnd], widthSize); - } - - return YGResolveValueMargin(YGComputedEdgeValue(node->style.margin, trailing[axis], &YGValueZero), - widthSize); -} - -static float YGNodeLeadingPadding(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeStart].unit != YGUnitUndefined && - YGResolveValue(&node->style.padding[YGEdgeStart], widthSize) >= 0.0f) { - return YGResolveValue(&node->style.padding[YGEdgeStart], widthSize); - } - - return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, leading[axis], &YGValueZero), - widthSize), - 0.0f); -} - -static float YGNodeTrailingPadding(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - if (YGFlexDirectionIsRow(axis) && node->style.padding[YGEdgeEnd].unit != YGUnitUndefined && - YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize) >= 0.0f) { - return YGResolveValue(&node->style.padding[YGEdgeEnd], widthSize); - } - - return fmaxf(YGResolveValue(YGComputedEdgeValue(node->style.padding, trailing[axis], &YGValueZero), - widthSize), - 0.0f); -} - -static float YGNodeLeadingBorder(const YGNodeRef node, const YGFlexDirection axis) { - if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeStart].unit != YGUnitUndefined && - node->style.border[YGEdgeStart].value >= 0.0f) { - return node->style.border[YGEdgeStart].value; - } - - return fmaxf(YGComputedEdgeValue(node->style.border, leading[axis], &YGValueZero)->value, 0.0f); -} - -static float YGNodeTrailingBorder(const YGNodeRef node, const YGFlexDirection axis) { - if (YGFlexDirectionIsRow(axis) && node->style.border[YGEdgeEnd].unit != YGUnitUndefined && - node->style.border[YGEdgeEnd].value >= 0.0f) { - return node->style.border[YGEdgeEnd].value; - } - - return fmaxf(YGComputedEdgeValue(node->style.border, trailing[axis], &YGValueZero)->value, 0.0f); -} - -static inline float YGNodeLeadingPaddingAndBorder(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - return YGNodeLeadingPadding(node, axis, widthSize) + YGNodeLeadingBorder(node, axis); -} - -static inline float YGNodeTrailingPaddingAndBorder(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - return YGNodeTrailingPadding(node, axis, widthSize) + YGNodeTrailingBorder(node, axis); -} - -static inline float YGNodeMarginForAxis(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - return YGNodeLeadingMargin(node, axis, widthSize) + YGNodeTrailingMargin(node, axis, widthSize); -} - -static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - return YGNodeLeadingPaddingAndBorder(node, axis, widthSize) + - YGNodeTrailingPaddingAndBorder(node, axis, widthSize); -} - -static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) { - const YGAlign align = - child->style.alignSelf == YGAlignAuto ? node->style.alignItems : child->style.alignSelf; - if (align == YGAlignBaseline && YGFlexDirectionIsColumn(node->style.flexDirection)) { - return YGAlignFlexStart; - } - return align; -} - -static inline YGDirection YGNodeResolveDirection(const YGNodeRef node, - const YGDirection parentDirection) { - if (node->style.direction == YGDirectionInherit) { - return parentDirection > YGDirectionInherit ? parentDirection : YGDirectionLTR; - } else { - return node->style.direction; - } -} - -static float YGBaseline(const YGNodeRef node) { - if (node->baseline != NULL) { - const float baseline = node->baseline(node, - node->layout.measuredDimensions[YGDimensionWidth], - node->layout.measuredDimensions[YGDimensionHeight]); - YGAssertWithNode(node, - !YGFloatIsUndefined(baseline), - "Expect custom baseline function to not return NaN"); - return baseline; - } - - YGNodeRef baselineChild = NULL; - const uint32_t childCount = YGNodeGetChildCount(node); - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeGetChild(node, i); - if (child->lineIndex > 0) { - break; - } - if (child->style.positionType == YGPositionTypeAbsolute) { - continue; - } - if (YGNodeAlignItem(node, child) == YGAlignBaseline) { - baselineChild = child; - break; - } - - if (baselineChild == NULL) { - baselineChild = child; - } - } - - if (baselineChild == NULL) { - return node->layout.measuredDimensions[YGDimensionHeight]; - } - - const float baseline = YGBaseline(baselineChild); - return baseline + baselineChild->layout.position[YGEdgeTop]; -} - -static inline YGFlexDirection YGResolveFlexDirection(const YGFlexDirection flexDirection, - const YGDirection direction) { - if (direction == YGDirectionRTL) { - if (flexDirection == YGFlexDirectionRow) { - return YGFlexDirectionRowReverse; - } else if (flexDirection == YGFlexDirectionRowReverse) { - return YGFlexDirectionRow; - } - } - - return flexDirection; -} - -static YGFlexDirection YGFlexDirectionCross(const YGFlexDirection flexDirection, - const YGDirection direction) { - return YGFlexDirectionIsColumn(flexDirection) - ? YGResolveFlexDirection(YGFlexDirectionRow, direction) - : YGFlexDirectionColumn; -} - -static inline bool YGNodeIsFlex(const YGNodeRef node) { - return (node->style.positionType == YGPositionTypeRelative && - (YGResolveFlexGrow(node) != 0 || YGNodeResolveFlexShrink(node) != 0)); -} - -static bool YGIsBaselineLayout(const YGNodeRef node) { - if (YGFlexDirectionIsColumn(node->style.flexDirection)) { - return false; - } - if (node->style.alignItems == YGAlignBaseline) { - return true; - } - const uint32_t childCount = YGNodeGetChildCount(node); - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeGetChild(node, i); - if (child->style.positionType == YGPositionTypeRelative && - child->style.alignSelf == YGAlignBaseline) { - return true; - } - } - - return false; -} - -static inline float YGNodeDimWithMargin(const YGNodeRef node, - const YGFlexDirection axis, - const float widthSize) { - return node->layout.measuredDimensions[dim[axis]] + YGNodeLeadingMargin(node, axis, widthSize) + - YGNodeTrailingMargin(node, axis, widthSize); -} - -static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node, - const YGFlexDirection axis, - const float parentSize) { - return !(node->resolvedDimensions[dim[axis]]->unit == YGUnitAuto || - node->resolvedDimensions[dim[axis]]->unit == YGUnitUndefined || - (node->resolvedDimensions[dim[axis]]->unit == YGUnitPoint && - node->resolvedDimensions[dim[axis]]->value < 0.0f) || - (node->resolvedDimensions[dim[axis]]->unit == YGUnitPercent && - (node->resolvedDimensions[dim[axis]]->value < 0.0f || YGFloatIsUndefined(parentSize)))); -} - -static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) { - const float value = node->layout.measuredDimensions[dim[axis]]; - return !YGFloatIsUndefined(value) && value >= 0.0f; -} - -static inline bool YGNodeIsLeadingPosDefined(const YGNodeRef node, const YGFlexDirection axis) { - return (YGFlexDirectionIsRow(axis) && - YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined)->unit != - YGUnitUndefined) || - YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined)->unit != - YGUnitUndefined; -} - -static inline bool YGNodeIsTrailingPosDefined(const YGNodeRef node, const YGFlexDirection axis) { - return (YGFlexDirectionIsRow(axis) && - YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined)->unit != - YGUnitUndefined) || - YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined)->unit != - YGUnitUndefined; -} - -static float YGNodeLeadingPosition(const YGNodeRef node, - const YGFlexDirection axis, - const float axisSize) { - if (YGFlexDirectionIsRow(axis)) { - const YGValue *leadingPosition = - YGComputedEdgeValue(node->style.position, YGEdgeStart, &YGValueUndefined); - if (leadingPosition->unit != YGUnitUndefined) { - return YGResolveValue(leadingPosition, axisSize); - } - } - - const YGValue *leadingPosition = - YGComputedEdgeValue(node->style.position, leading[axis], &YGValueUndefined); - - return leadingPosition->unit == YGUnitUndefined ? 0.0f - : YGResolveValue(leadingPosition, axisSize); -} - -static float YGNodeTrailingPosition(const YGNodeRef node, - const YGFlexDirection axis, - const float axisSize) { - if (YGFlexDirectionIsRow(axis)) { - const YGValue *trailingPosition = - YGComputedEdgeValue(node->style.position, YGEdgeEnd, &YGValueUndefined); - if (trailingPosition->unit != YGUnitUndefined) { - return YGResolveValue(trailingPosition, axisSize); - } - } - - const YGValue *trailingPosition = - YGComputedEdgeValue(node->style.position, trailing[axis], &YGValueUndefined); - - return trailingPosition->unit == YGUnitUndefined ? 0.0f - : YGResolveValue(trailingPosition, axisSize); -} - -static float YGNodeBoundAxisWithinMinAndMax(const YGNodeRef node, - const YGFlexDirection axis, - const float value, - const float axisSize) { - float min = YGUndefined; - float max = YGUndefined; - - if (YGFlexDirectionIsColumn(axis)) { - min = YGResolveValue(&node->style.minDimensions[YGDimensionHeight], axisSize); - max = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], axisSize); - } else if (YGFlexDirectionIsRow(axis)) { - min = YGResolveValue(&node->style.minDimensions[YGDimensionWidth], axisSize); - max = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], axisSize); - } - - float boundValue = value; - - if (!YGFloatIsUndefined(max) && max >= 0.0f && boundValue > max) { - boundValue = max; - } - - if (!YGFloatIsUndefined(min) && min >= 0.0f && boundValue < min) { - boundValue = min; - } - - return boundValue; -} - -static inline YGValue *YGMarginLeadingValue(const YGNodeRef node, const YGFlexDirection axis) { - if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeStart].unit != YGUnitUndefined) { - return &node->style.margin[YGEdgeStart]; - } else { - return &node->style.margin[leading[axis]]; - } -} - -static inline YGValue *YGMarginTrailingValue(const YGNodeRef node, const YGFlexDirection axis) { - if (YGFlexDirectionIsRow(axis) && node->style.margin[YGEdgeEnd].unit != YGUnitUndefined) { - return &node->style.margin[YGEdgeEnd]; - } else { - return &node->style.margin[trailing[axis]]; - } -} - -// Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go -// below the -// padding and border amount. -static inline float YGNodeBoundAxis(const YGNodeRef node, - const YGFlexDirection axis, - const float value, - const float axisSize, - const float widthSize) { - return fmaxf(YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), - YGNodePaddingAndBorderForAxis(node, axis, widthSize)); -} - -static void YGNodeSetChildTrailingPosition(const YGNodeRef node, - const YGNodeRef child, - const YGFlexDirection axis) { - const float size = child->layout.measuredDimensions[dim[axis]]; - child->layout.position[trailing[axis]] = - node->layout.measuredDimensions[dim[axis]] - size - child->layout.position[pos[axis]]; -} - -// If both left and right are defined, then use left. Otherwise return -// +left or -right depending on which is defined. -static float YGNodeRelativePosition(const YGNodeRef node, - const YGFlexDirection axis, - const float axisSize) { - return YGNodeIsLeadingPosDefined(node, axis) ? YGNodeLeadingPosition(node, axis, axisSize) - : -YGNodeTrailingPosition(node, axis, axisSize); -} - -static void YGConstrainMaxSizeForMode(const YGNodeRef node, - const enum YGFlexDirection axis, - const float parentAxisSize, - const float parentWidth, - YGMeasureMode *mode, - float *size) { - const float maxSize = YGResolveValue(&node->style.maxDimensions[dim[axis]], parentAxisSize) + - YGNodeMarginForAxis(node, axis, parentWidth); - switch (*mode) { - case YGMeasureModeExactly: - case YGMeasureModeAtMost: - *size = (YGFloatIsUndefined(maxSize) || *size < maxSize) ? *size : maxSize; - break; - case YGMeasureModeUndefined: - if (!YGFloatIsUndefined(maxSize)) { - *mode = YGMeasureModeAtMost; - *size = maxSize; - } - break; - } -} - -static void YGNodeSetPosition(const YGNodeRef node, - const YGDirection direction, - const float mainSize, - const float crossSize, - const float parentWidth) { - /* Root nodes should be always layouted as LTR, so we don't return negative values. */ - const YGDirection directionRespectingRoot = node->parent != NULL ? direction : YGDirectionLTR; - const YGFlexDirection mainAxis = - YGResolveFlexDirection(node->style.flexDirection, directionRespectingRoot); - const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, directionRespectingRoot); - - const float relativePositionMain = YGNodeRelativePosition(node, mainAxis, mainSize); - const float relativePositionCross = YGNodeRelativePosition(node, crossAxis, crossSize); - - node->layout.position[leading[mainAxis]] = - YGNodeLeadingMargin(node, mainAxis, parentWidth) + relativePositionMain; - node->layout.position[trailing[mainAxis]] = - YGNodeTrailingMargin(node, mainAxis, parentWidth) + relativePositionMain; - node->layout.position[leading[crossAxis]] = - YGNodeLeadingMargin(node, crossAxis, parentWidth) + relativePositionCross; - node->layout.position[trailing[crossAxis]] = - YGNodeTrailingMargin(node, crossAxis, parentWidth) + relativePositionCross; -} - -static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, - const YGNodeRef child, - const float width, - const YGMeasureMode widthMode, - const float height, - const float parentWidth, - const float parentHeight, - const YGMeasureMode heightMode, - const YGDirection direction, - const YGConfigRef config) { - const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); - const float mainAxisSize = isMainAxisRow ? width : height; - const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; - - float childWidth; - float childHeight; - YGMeasureMode childWidthMeasureMode; - YGMeasureMode childHeightMeasureMode; - - const float resolvedFlexBasis = - YGResolveValue(YGNodeResolveFlexBasisPtr(child), mainAxisParentSize); - const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, parentWidth); - const bool isColumnStyleDimDefined = - YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, parentHeight); - - if (!YGFloatIsUndefined(resolvedFlexBasis) && !YGFloatIsUndefined(mainAxisSize)) { - if (YGFloatIsUndefined(child->layout.computedFlexBasis) || - (YGConfigIsExperimentalFeatureEnabled(child->config, YGExperimentalFeatureWebFlexBasis) && - child->layout.computedFlexBasisGeneration != gCurrentGenerationCount)) { - child->layout.computedFlexBasis = - fmaxf(resolvedFlexBasis, YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth)); - } - } else if (isMainAxisRow && isRowStyleDimDefined) { - // The width is definite, so use that as the flex basis. - child->layout.computedFlexBasis = - fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth), - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth)); - } else if (!isMainAxisRow && isColumnStyleDimDefined) { - // The height is definite, so use that as the flex basis. - child->layout.computedFlexBasis = - fmaxf(YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight), - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, parentWidth)); - } else { - // Compute the flex basis and hypothetical main size (i.e. the clamped - // flex basis). - childWidth = YGUndefined; - childHeight = YGUndefined; - childWidthMeasureMode = YGMeasureModeUndefined; - childHeightMeasureMode = YGMeasureModeUndefined; - - const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, parentWidth); - const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, parentWidth); - - if (isRowStyleDimDefined) { - childWidth = - YGResolveValue(child->resolvedDimensions[YGDimensionWidth], parentWidth) + marginRow; - childWidthMeasureMode = YGMeasureModeExactly; - } - if (isColumnStyleDimDefined) { - childHeight = - YGResolveValue(child->resolvedDimensions[YGDimensionHeight], parentHeight) + marginColumn; - childHeightMeasureMode = YGMeasureModeExactly; - } - - // The W3C spec doesn't say anything about the 'overflow' property, - // but all major browsers appear to implement the following logic. - if ((!isMainAxisRow && node->style.overflow == YGOverflowScroll) || - node->style.overflow != YGOverflowScroll) { - if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) { - childWidth = width; - childWidthMeasureMode = YGMeasureModeAtMost; - } - } - - if ((isMainAxisRow && node->style.overflow == YGOverflowScroll) || - node->style.overflow != YGOverflowScroll) { - if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) { - childHeight = height; - childHeightMeasureMode = YGMeasureModeAtMost; - } - } - - // If child has no defined size in the cross axis and is set to stretch, - // set the cross - // axis to be measured exactly with the available inner width - if (!isMainAxisRow && !YGFloatIsUndefined(width) && !isRowStyleDimDefined && - widthMode == YGMeasureModeExactly && YGNodeAlignItem(node, child) == YGAlignStretch) { - childWidth = width; - childWidthMeasureMode = YGMeasureModeExactly; - } - if (isMainAxisRow && !YGFloatIsUndefined(height) && !isColumnStyleDimDefined && - heightMode == YGMeasureModeExactly && YGNodeAlignItem(node, child) == YGAlignStretch) { - childHeight = height; - childHeightMeasureMode = YGMeasureModeExactly; - } - - if (!YGFloatIsUndefined(child->style.aspectRatio)) { - if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) { - child->layout.computedFlexBasis = - fmaxf((childWidth - marginRow) / child->style.aspectRatio, - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, parentWidth)); - return; - } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) { - child->layout.computedFlexBasis = - fmaxf((childHeight - marginColumn) * child->style.aspectRatio, - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth)); - return; - } - } - - YGConstrainMaxSizeForMode( - child, YGFlexDirectionRow, parentWidth, parentWidth, &childWidthMeasureMode, &childWidth); - YGConstrainMaxSizeForMode(child, - YGFlexDirectionColumn, - parentHeight, - parentWidth, - &childHeightMeasureMode, - &childHeight); - - // Measure the child - YGLayoutNodeInternal(child, - childWidth, - childHeight, - direction, - childWidthMeasureMode, - childHeightMeasureMode, - parentWidth, - parentHeight, - false, - "measure", - config); - - child->layout.computedFlexBasis = - fmaxf(child->layout.measuredDimensions[dim[mainAxis]], - YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth)); - } - - child->layout.computedFlexBasisGeneration = gCurrentGenerationCount; -} - -static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, - const YGNodeRef child, - const float width, - const YGMeasureMode widthMode, - const float height, - const YGDirection direction, - const YGConfigRef config) { - const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); - const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); - - float childWidth = YGUndefined; - float childHeight = YGUndefined; - YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined; - YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined; - - const float marginRow = YGNodeMarginForAxis(child, YGFlexDirectionRow, width); - const float marginColumn = YGNodeMarginForAxis(child, YGFlexDirectionColumn, width); - - if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) { - childWidth = YGResolveValue(child->resolvedDimensions[YGDimensionWidth], width) + marginRow; - } else { - // If the child doesn't have a specified width, compute the width based - // on the left/right - // offsets if they're defined. - if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionRow) && - YGNodeIsTrailingPosDefined(child, YGFlexDirectionRow)) { - childWidth = node->layout.measuredDimensions[YGDimensionWidth] - - (YGNodeLeadingBorder(node, YGFlexDirectionRow) + - YGNodeTrailingBorder(node, YGFlexDirectionRow)) - - (YGNodeLeadingPosition(child, YGFlexDirectionRow, width) + - YGNodeTrailingPosition(child, YGFlexDirectionRow, width)); - childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width); - } - } - - if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { - childHeight = - YGResolveValue(child->resolvedDimensions[YGDimensionHeight], height) + marginColumn; - } else { - // If the child doesn't have a specified height, compute the height - // based on the top/bottom - // offsets if they're defined. - if (YGNodeIsLeadingPosDefined(child, YGFlexDirectionColumn) && - YGNodeIsTrailingPosDefined(child, YGFlexDirectionColumn)) { - childHeight = node->layout.measuredDimensions[YGDimensionHeight] - - (YGNodeLeadingBorder(node, YGFlexDirectionColumn) + - YGNodeTrailingBorder(node, YGFlexDirectionColumn)) - - (YGNodeLeadingPosition(child, YGFlexDirectionColumn, height) + - YGNodeTrailingPosition(child, YGFlexDirectionColumn, height)); - childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width); - } - } - - // 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 (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) { - if (!YGFloatIsUndefined(child->style.aspectRatio)) { - if (YGFloatIsUndefined(childWidth)) { - childWidth = - marginRow + fmaxf((childHeight - marginColumn) * child->style.aspectRatio, - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionColumn, width)); - } else if (YGFloatIsUndefined(childHeight)) { - childHeight = - marginColumn + fmaxf((childWidth - marginRow) / child->style.aspectRatio, - YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, width)); - } - } - } - - // If we're still missing one or the other dimension, measure the content. - if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) { - childWidthMeasureMode = - YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly; - childHeightMeasureMode = - YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly; - - // If the size of the parent is defined then try to constrain the absolute child to that size - // as well. This allows text within the absolute child to wrap to the size of its parent. - // This is the same behavior as many browsers implement. - if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && widthMode != YGMeasureModeUndefined && - width > 0) { - childWidth = width; - childWidthMeasureMode = YGMeasureModeAtMost; - } - - YGLayoutNodeInternal(child, - childWidth, - childHeight, - direction, - childWidthMeasureMode, - childHeightMeasureMode, - childWidth, - childHeight, - false, - "abs-measure", - config); - childWidth = child->layout.measuredDimensions[YGDimensionWidth] + - YGNodeMarginForAxis(child, YGFlexDirectionRow, width); - childHeight = child->layout.measuredDimensions[YGDimensionHeight] + - YGNodeMarginForAxis(child, YGFlexDirectionColumn, width); - } - - YGLayoutNodeInternal(child, - childWidth, - childHeight, - direction, - YGMeasureModeExactly, - YGMeasureModeExactly, - childWidth, - childHeight, - true, - "abs-layout", - config); - - if (YGNodeIsTrailingPosDefined(child, mainAxis) && !YGNodeIsLeadingPosDefined(child, mainAxis)) { - child->layout.position[leading[mainAxis]] = node->layout.measuredDimensions[dim[mainAxis]] - - child->layout.measuredDimensions[dim[mainAxis]] - - YGNodeTrailingBorder(node, mainAxis) - - YGNodeTrailingMargin(child, mainAxis, width) - - YGNodeTrailingPosition(child, mainAxis, isMainAxisRow ? width : height); - } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) && - node->style.justifyContent == YGJustifyCenter) { - child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] - - child->layout.measuredDimensions[dim[mainAxis]]) / - 2.0f; - } else if (!YGNodeIsLeadingPosDefined(child, mainAxis) && - node->style.justifyContent == YGJustifyFlexEnd) { - child->layout.position[leading[mainAxis]] = (node->layout.measuredDimensions[dim[mainAxis]] - - child->layout.measuredDimensions[dim[mainAxis]]); - } - - if (YGNodeIsTrailingPosDefined(child, crossAxis) && - !YGNodeIsLeadingPosDefined(child, crossAxis)) { - child->layout.position[leading[crossAxis]] = node->layout.measuredDimensions[dim[crossAxis]] - - child->layout.measuredDimensions[dim[crossAxis]] - - YGNodeTrailingBorder(node, crossAxis) - - YGNodeTrailingMargin(child, crossAxis, width) - - YGNodeTrailingPosition(child, crossAxis, isMainAxisRow ? height : width); - } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) && - YGNodeAlignItem(node, child) == YGAlignCenter) { - child->layout.position[leading[crossAxis]] = - (node->layout.measuredDimensions[dim[crossAxis]] - - child->layout.measuredDimensions[dim[crossAxis]]) / - 2.0f; - } else if (!YGNodeIsLeadingPosDefined(child, crossAxis) && - ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^ (node->style.flexWrap == YGWrapWrapReverse))) { - child->layout.position[leading[crossAxis]] = (node->layout.measuredDimensions[dim[crossAxis]] - - child->layout.measuredDimensions[dim[crossAxis]]); - } -} - -static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight) { - YGAssertWithNode(node, node->measure != NULL, "Expected node to have custom measure function"); - - const float paddingAndBorderAxisRow = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth); - const float paddingAndBorderAxisColumn = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth); - const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, availableWidth); - const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, availableWidth); - - // We want to make sure we don't call measure with negative size - const float innerWidth = YGFloatIsUndefined(availableWidth) - ? availableWidth - : fmaxf(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); - const float innerHeight = YGFloatIsUndefined(availableHeight) - ? availableHeight - : fmaxf(0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); - - if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) { - // Don't bother sizing the text if both dimensions are already defined. - node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis( - node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth); - node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis( - node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth); - } else { - // Measure the text under the current constraints. - const YGSize measuredSize = - node->measure(node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode); - - node->layout.measuredDimensions[YGDimensionWidth] = - YGNodeBoundAxis(node, - YGFlexDirectionRow, - (widthMeasureMode == YGMeasureModeUndefined || - widthMeasureMode == YGMeasureModeAtMost) - ? measuredSize.width + paddingAndBorderAxisRow - : availableWidth - marginAxisRow, - availableWidth, - availableWidth); - node->layout.measuredDimensions[YGDimensionHeight] = - YGNodeBoundAxis(node, - YGFlexDirectionColumn, - (heightMeasureMode == YGMeasureModeUndefined || - heightMeasureMode == YGMeasureModeAtMost) - ? measuredSize.height + paddingAndBorderAxisColumn - : availableHeight - marginAxisColumn, - availableHeight, - availableWidth); - } -} - -// For nodes with no children, use the available values if they were provided, -// or the minimum size as indicated by the padding and border sizes. -static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight) { - const float paddingAndBorderAxisRow = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, parentWidth); - const float paddingAndBorderAxisColumn = - YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, parentWidth); - const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); - const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); - - node->layout.measuredDimensions[YGDimensionWidth] = - YGNodeBoundAxis(node, - YGFlexDirectionRow, - (widthMeasureMode == YGMeasureModeUndefined || - widthMeasureMode == YGMeasureModeAtMost) - ? paddingAndBorderAxisRow - : availableWidth - marginAxisRow, - parentWidth, - parentWidth); - node->layout.measuredDimensions[YGDimensionHeight] = - YGNodeBoundAxis(node, - YGFlexDirectionColumn, - (heightMeasureMode == YGMeasureModeUndefined || - heightMeasureMode == YGMeasureModeAtMost) - ? paddingAndBorderAxisColumn - : availableHeight - marginAxisColumn, - parentHeight, - parentWidth); -} - -static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight) { - if ((widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || - (heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || - (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) { - const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); - const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); - - node->layout.measuredDimensions[YGDimensionWidth] = - YGNodeBoundAxis(node, - YGFlexDirectionRow, - YGFloatIsUndefined(availableWidth) || - (widthMeasureMode == YGMeasureModeAtMost && availableWidth < 0.0f) - ? 0.0f - : availableWidth - marginAxisRow, - parentWidth, - parentWidth); - - node->layout.measuredDimensions[YGDimensionHeight] = - YGNodeBoundAxis(node, - YGFlexDirectionColumn, - YGFloatIsUndefined(availableHeight) || - (heightMeasureMode == YGMeasureModeAtMost && availableHeight < 0.0f) - ? 0.0f - : availableHeight - marginAxisColumn, - parentHeight, - parentWidth); - - return true; - } - - return false; -} - -static void YGZeroOutLayoutRecursivly(const YGNodeRef node) { - node->layout.dimensions[YGDimensionHeight] = 0; - node->layout.dimensions[YGDimensionWidth] = 0; - node->layout.position[YGEdgeTop] = 0; - node->layout.position[YGEdgeBottom] = 0; - node->layout.position[YGEdgeLeft] = 0; - node->layout.position[YGEdgeRight] = 0; - node->layout.cachedLayout.availableHeight = 0; - node->layout.cachedLayout.availableWidth = 0; - node->layout.cachedLayout.heightMeasureMode = YGMeasureModeExactly; - node->layout.cachedLayout.widthMeasureMode = YGMeasureModeExactly; - node->layout.cachedLayout.computedWidth = 0; - node->layout.cachedLayout.computedHeight = 0; - node->hasNewLayout = true; - const uint32_t childCount = YGNodeGetChildCount(node); - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - YGZeroOutLayoutRecursivly(child); - } -} - -// -// This is the main routine that implements a subset of the flexbox layout -// algorithm -// described in the W3C YG documentation: https://www.w3.org/TR/YG3-flexbox/. -// -// Limitations of this algorithm, compared to the full standard: -// * Display property is always assumed to be 'flex' except for Text nodes, -// which -// are assumed to be 'inline-flex'. -// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes -// are -// stacked in document order. -// * The 'order' property is not supported. The order of flex items is always -// defined -// by document order. -// * The 'visibility' property is always assumed to be 'visible'. Values of -// 'collapse' -// and 'hidden' are not supported. -// * There is no support for forced breaks. -// * It does not support vertical inline directions (top-to-bottom or -// bottom-to-top text). -// -// Deviations from standard: -// * Section 4.5 of the spec indicates that all flex items have a default -// minimum -// main size. For text blocks, for example, this is the width of the widest -// word. -// Calculating the minimum width is expensive, so we forego it and assume a -// default -// minimum main size of 0. -// * Min/Max sizes in the main axis are not honored when resolving flexible -// lengths. -// * The spec indicates that the default value for 'flexDirection' is 'row', -// but -// the algorithm below assumes a default of 'column'. -// -// Input parameters: -// - node: current node to be sized and layed out -// - availableWidth & availableHeight: available size to be used for sizing -// the node -// or YGUndefined if the size is not available; interpretation depends on -// layout -// flags -// - parentDirection: the inline (text) direction within the parent -// (left-to-right or -// right-to-left) -// - widthMeasureMode: indicates the sizing rules for the width (see below -// for explanation) -// - heightMeasureMode: indicates the sizing rules for the height (see below -// for explanation) -// - performLayout: specifies whether the caller is interested in just the -// dimensions -// of the node or it requires the entire node and its subtree to be layed -// out -// (with final positions) -// -// Details: -// This routine is called recursively to lay out subtrees of flexbox -// elements. It uses the -// information in node.style, which is treated as a read-only input. It is -// responsible for -// setting the layout.direction and layout.measuredDimensions fields for the -// input node as well -// as the layout.position and layout.lineIndex fields for its child nodes. -// The -// layout.measuredDimensions field includes any border or padding for the -// node but does -// not include margins. -// -// The spec describes four different layout modes: "fill available", "max -// content", "min -// content", -// and "fit content". Of these, we don't use "min content" because we don't -// support default -// minimum main sizes (see above for details). Each of our measure modes maps -// to a layout mode -// from the spec (https://www.w3.org/TR/YG3-sizing/#terms): -// - YGMeasureModeUndefined: max content -// - YGMeasureModeExactly: fill available -// - YGMeasureModeAtMost: fit content -// -// When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes -// an available size of -// undefined then it must also pass a measure mode of YGMeasureModeUndefined -// in that dimension. -// -static void YGNodelayoutImpl(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGDirection parentDirection, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight, - const bool performLayout, - const YGConfigRef config) { - YGAssertWithNode(node, - YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined - : true, - "availableWidth is indefinite so widthMeasureMode must be " - "YGMeasureModeUndefined"); - YGAssertWithNode(node, - YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined - : true, - "availableHeight is indefinite so heightMeasureMode must be " - "YGMeasureModeUndefined"); - - // Set the resolved resolution in the node's layout. - const YGDirection direction = YGNodeResolveDirection(node, parentDirection); - node->layout.direction = direction; - - const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction); - const YGFlexDirection flexColumnDirection = - YGResolveFlexDirection(YGFlexDirectionColumn, direction); - - node->layout.margin[YGEdgeStart] = YGNodeLeadingMargin(node, flexRowDirection, parentWidth); - node->layout.margin[YGEdgeEnd] = YGNodeTrailingMargin(node, flexRowDirection, parentWidth); - node->layout.margin[YGEdgeTop] = YGNodeLeadingMargin(node, flexColumnDirection, parentWidth); - node->layout.margin[YGEdgeBottom] = YGNodeTrailingMargin(node, flexColumnDirection, parentWidth); - - node->layout.border[YGEdgeStart] = YGNodeLeadingBorder(node, flexRowDirection); - node->layout.border[YGEdgeEnd] = YGNodeTrailingBorder(node, flexRowDirection); - node->layout.border[YGEdgeTop] = YGNodeLeadingBorder(node, flexColumnDirection); - node->layout.border[YGEdgeBottom] = YGNodeTrailingBorder(node, flexColumnDirection); - - node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, flexRowDirection, parentWidth); - node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, flexRowDirection, parentWidth); - node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, flexColumnDirection, parentWidth); - node->layout.padding[YGEdgeBottom] = - YGNodeTrailingPadding(node, flexColumnDirection, parentWidth); - - if (node->measure) { - YGNodeWithMeasureFuncSetMeasuredDimensions(node, - availableWidth, - availableHeight, - widthMeasureMode, - heightMeasureMode, - parentWidth, - parentHeight); - return; - } - - const uint32_t childCount = YGNodeListCount(node->children); - if (childCount == 0) { - YGNodeEmptyContainerSetMeasuredDimensions(node, - availableWidth, - availableHeight, - widthMeasureMode, - heightMeasureMode, - parentWidth, - parentHeight); - return; - } - - // If we're not being asked to perform a full layout we can skip the algorithm if we already know - // the size - if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node, - availableWidth, - availableHeight, - widthMeasureMode, - heightMeasureMode, - parentWidth, - parentHeight)) { - return; - } - - // Reset layout flags, as they could have changed. - node->layout.hadOverflow = false; - - // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM - const YGFlexDirection mainAxis = YGResolveFlexDirection(node->style.flexDirection, direction); - const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); - const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); - const YGJustify justifyContent = node->style.justifyContent; - const bool isNodeFlexWrap = node->style.flexWrap != YGWrapNoWrap; - - const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; - const float crossAxisParentSize = isMainAxisRow ? parentHeight : parentWidth; - - YGNodeRef firstAbsoluteChild = NULL; - YGNodeRef currentAbsoluteChild = NULL; - - const float leadingPaddingAndBorderMain = - YGNodeLeadingPaddingAndBorder(node, mainAxis, parentWidth); - const float trailingPaddingAndBorderMain = - YGNodeTrailingPaddingAndBorder(node, mainAxis, parentWidth); - const float leadingPaddingAndBorderCross = - YGNodeLeadingPaddingAndBorder(node, crossAxis, parentWidth); - const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, parentWidth); - const float paddingAndBorderAxisCross = - YGNodePaddingAndBorderForAxis(node, crossAxis, parentWidth); - - YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; - YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; - - const float paddingAndBorderAxisRow = - isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; - const float paddingAndBorderAxisColumn = - isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; - - const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); - const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); - - // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS - const float minInnerWidth = - YGResolveValue(&node->style.minDimensions[YGDimensionWidth], parentWidth) - marginAxisRow - - paddingAndBorderAxisRow; - const float maxInnerWidth = - YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) - marginAxisRow - - paddingAndBorderAxisRow; - const float minInnerHeight = - YGResolveValue(&node->style.minDimensions[YGDimensionHeight], parentHeight) - - marginAxisColumn - paddingAndBorderAxisColumn; - const float maxInnerHeight = - YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) - - marginAxisColumn - paddingAndBorderAxisColumn; - const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight; - const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight; - - // Max dimension overrides predefined dimension value; Min dimension in turn overrides both of the - // above - float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; - if (!YGFloatIsUndefined(availableInnerWidth)) { - // We want to make sure our available width does not violate min and max constraints - availableInnerWidth = fmaxf(fminf(availableInnerWidth, maxInnerWidth), minInnerWidth); - } - - float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; - if (!YGFloatIsUndefined(availableInnerHeight)) { - // We want to make sure our available height does not violate min and max constraints - availableInnerHeight = fmaxf(fminf(availableInnerHeight, maxInnerHeight), minInnerHeight); - } - - float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; - const float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; - - // If there is only one child with flexGrow + flexShrink it means we can set the - // computedFlexBasis to 0 instead of measuring and shrinking / flexing the child to exactly - // match the remaining space - YGNodeRef singleFlexChild = NULL; - if (measureModeMainDim == YGMeasureModeExactly) { - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeGetChild(node, i); - if (singleFlexChild) { - if (YGNodeIsFlex(child)) { - // There is already a flexible child, abort. - singleFlexChild = NULL; - break; - } - } else if (YGResolveFlexGrow(child) > 0.0f && YGNodeResolveFlexShrink(child) > 0.0f) { - singleFlexChild = child; - } - } - } - - float totalOuterFlexBasis = 0; - - // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.display == YGDisplayNone) { - YGZeroOutLayoutRecursivly(child); - child->hasNewLayout = true; - child->isDirty = false; - continue; - } - YGResolveDimensions(child); - if (performLayout) { - // Set the initial position (relative to the parent). - const YGDirection childDirection = YGNodeResolveDirection(child, direction); - YGNodeSetPosition(child, - childDirection, - availableInnerMainDim, - availableInnerCrossDim, - availableInnerWidth); - } - - // Absolute-positioned children don't participate in flex layout. Add them - // to a list that we can process later. - if (child->style.positionType == YGPositionTypeAbsolute) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild == NULL) { - firstAbsoluteChild = child; - } - if (currentAbsoluteChild != NULL) { - currentAbsoluteChild->nextChild = child; - } - currentAbsoluteChild = child; - child->nextChild = NULL; - } else { - if (child == singleFlexChild) { - child->layout.computedFlexBasisGeneration = gCurrentGenerationCount; - child->layout.computedFlexBasis = 0; - } else { - YGNodeComputeFlexBasisForChild(node, - child, - availableInnerWidth, - widthMeasureMode, - availableInnerHeight, - availableInnerWidth, - availableInnerHeight, - heightMeasureMode, - direction, - config); - } - } - - totalOuterFlexBasis += - child->layout.computedFlexBasis + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); - ; - } - - const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined - ? false - : totalOuterFlexBasis > availableInnerMainDim; - if (isNodeFlexWrap && flexBasisOverflows && measureModeMainDim == YGMeasureModeAtMost) { - measureModeMainDim = YGMeasureModeExactly; - } - - // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES - - // Indexes of children that represent the first and last items in the line. - uint32_t startOfLineIndex = 0; - uint32_t endOfLineIndex = 0; - - // Number of lines. - uint32_t lineCount = 0; - - // Accumulated cross dimensions of all lines so far. - float totalLineCrossDim = 0; - - // Max main dimension of all the lines. - float maxLineMainDim = 0; - - for (; endOfLineIndex < childCount; lineCount++, startOfLineIndex = endOfLineIndex) { - // Number of items on the currently line. May be different than the - // difference - // between start and end indicates because we skip over absolute-positioned - // items. - uint32_t itemsOnLine = 0; - - // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin - // of all the children on the current line. This will be used in order to - // either set the dimensions of the node if none already exist or to compute - // the remaining space left for the flexible children. - float sizeConsumedOnCurrentLine = 0; - float sizeConsumedOnCurrentLineIncludingMinConstraint = 0; - - float totalFlexGrowFactors = 0; - float totalFlexShrinkScaledFactors = 0; - - // Maintain a linked list of the child nodes that can shrink and/or grow. - YGNodeRef firstRelativeChild = NULL; - YGNodeRef currentRelativeChild = NULL; - - // Add items to the current line until it's full or we run out of items. - for (uint32_t i = startOfLineIndex; i < childCount; i++, endOfLineIndex++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.display == YGDisplayNone) { - continue; - } - child->lineIndex = lineCount; - - if (child->style.positionType != YGPositionTypeAbsolute) { - const float childMarginMainAxis = YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); - const float flexBasisWithMaxConstraints = - fminf(YGResolveValue(&child->style.maxDimensions[dim[mainAxis]], mainAxisParentSize), - child->layout.computedFlexBasis); - const float flexBasisWithMinAndMaxConstraints = - fmaxf(YGResolveValue(&child->style.minDimensions[dim[mainAxis]], mainAxisParentSize), - flexBasisWithMaxConstraints); - - // If this is a multi-line flow and this item pushes us over the - // available size, we've - // hit the end of the current line. Break out of the loop and lay out - // the current line. - if (sizeConsumedOnCurrentLineIncludingMinConstraint + flexBasisWithMinAndMaxConstraints + - childMarginMainAxis > - availableInnerMainDim && - isNodeFlexWrap && itemsOnLine > 0) { - break; - } - - sizeConsumedOnCurrentLineIncludingMinConstraint += - flexBasisWithMinAndMaxConstraints + childMarginMainAxis; - sizeConsumedOnCurrentLine += flexBasisWithMinAndMaxConstraints + childMarginMainAxis; - itemsOnLine++; - - if (YGNodeIsFlex(child)) { - totalFlexGrowFactors += YGResolveFlexGrow(child); - - // Unlike the grow factor, the shrink factor is scaled relative to the child dimension. - totalFlexShrinkScaledFactors += - -YGNodeResolveFlexShrink(child) * child->layout.computedFlexBasis; - } - - // Store a private linked list of children that need to be layed out. - if (firstRelativeChild == NULL) { - firstRelativeChild = child; - } - if (currentRelativeChild != NULL) { - currentRelativeChild->nextChild = child; - } - currentRelativeChild = child; - child->nextChild = NULL; - } - } - - // The total flex factor needs to be floored to 1. - if (totalFlexGrowFactors > 0 && totalFlexGrowFactors < 1) { - totalFlexGrowFactors = 1; - } - - // The total flex shrink factor needs to be floored to 1. - if (totalFlexShrinkScaledFactors > 0 && totalFlexShrinkScaledFactors < 1) { - totalFlexShrinkScaledFactors = 1; - } - - // If we don't need to measure the cross axis, we can skip the entire flex - // step. - const bool canSkipFlex = !performLayout && measureModeCrossDim == YGMeasureModeExactly; - - // In order to position the elements in the main axis, we have two - // controls. The space between the beginning and the first element - // and the space between each two elements. - float leadingMainDim = 0; - float betweenMainDim = 0; - - // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS - // Calculate the remaining available space that needs to be allocated. - // If the main dimension size isn't known, it is computed based on - // the line length, so there's no more space left to distribute. - - // If we don't measure with exact main dimension we want to ensure we don't violate min and max - if (measureModeMainDim != YGMeasureModeExactly) { - if (!YGFloatIsUndefined(minInnerMainDim) && sizeConsumedOnCurrentLine < minInnerMainDim) { - availableInnerMainDim = minInnerMainDim; - } else if (!YGFloatIsUndefined(maxInnerMainDim) && - sizeConsumedOnCurrentLine > maxInnerMainDim) { - availableInnerMainDim = maxInnerMainDim; - } else { - if (!node->config->useLegacyStretchBehaviour && - (totalFlexGrowFactors == 0 || YGResolveFlexGrow(node) == 0)) { - // If we don't have any children to flex or we can't flex the node itself, - // space we've used is all space we need. Root node also should be shrunk to minimum - availableInnerMainDim = sizeConsumedOnCurrentLine; - } - } - } - - float remainingFreeSpace = 0; - if (!YGFloatIsUndefined(availableInnerMainDim)) { - remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; - } else if (sizeConsumedOnCurrentLine < 0) { - // availableInnerMainDim is indefinite which means the node is being sized based on its - // content. - // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for - // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. - remainingFreeSpace = -sizeConsumedOnCurrentLine; - } - - const float originalRemainingFreeSpace = remainingFreeSpace; - float deltaFreeSpace = 0; - - if (!canSkipFlex) { - float childFlexBasis; - float flexShrinkScaledFactor; - float flexGrowFactor; - float baseMainSize; - float boundMainSize; - - // Do two passes over the flex items to figure out how to distribute the - // remaining space. - // The first pass finds the items whose min/max constraints trigger, - // freezes them at those - // sizes, and excludes those sizes from the remaining space. The second - // pass sets the size - // of each flexible item. It distributes the remaining space amongst the - // items whose min/max - // constraints didn't trigger in pass 1. For the other items, it sets - // their sizes by forcing - // their min/max constraints to trigger again. - // - // This two pass approach for resolving min/max constraints deviates from - // the spec. The - // spec (https://www.w3.org/TR/YG-flexbox-1/#resolve-flexible-lengths) - // describes a process - // that needs to be repeated a variable number of times. The algorithm - // implemented here - // won't handle all cases but it was simpler to implement and it mitigates - // performance - // concerns because we know exactly how many passes it'll do. - - // First pass: detect the flex items whose min/max constraints trigger - float deltaFlexShrinkScaledFactors = 0; - float deltaFlexGrowFactors = 0; - currentRelativeChild = firstRelativeChild; - while (currentRelativeChild != NULL) { - childFlexBasis = - fminf(YGResolveValue(¤tRelativeChild->style.maxDimensions[dim[mainAxis]], - mainAxisParentSize), - fmaxf(YGResolveValue(¤tRelativeChild->style.minDimensions[dim[mainAxis]], - mainAxisParentSize), - currentRelativeChild->layout.computedFlexBasis)); - - if (remainingFreeSpace < 0) { - flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis; - - // Is this child able to shrink? - if (flexShrinkScaledFactor != 0) { - baseMainSize = - childFlexBasis + - remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; - boundMainSize = YGNodeBoundAxis(currentRelativeChild, - mainAxis, - baseMainSize, - availableInnerMainDim, - availableInnerWidth); - if (baseMainSize != boundMainSize) { - // By excluding this item's size and flex factor from remaining, - // this item's - // min/max constraints should also trigger in the second pass - // resulting in the - // item's size calculation being identical in the first and second - // passes. - deltaFreeSpace -= boundMainSize - childFlexBasis; - deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; - } - } - } else if (remainingFreeSpace > 0) { - flexGrowFactor = YGResolveFlexGrow(currentRelativeChild); - - // Is this child able to grow? - if (flexGrowFactor != 0) { - baseMainSize = - childFlexBasis + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; - boundMainSize = YGNodeBoundAxis(currentRelativeChild, - mainAxis, - baseMainSize, - availableInnerMainDim, - availableInnerWidth); - - if (baseMainSize != boundMainSize) { - // By excluding this item's size and flex factor from remaining, - // this item's - // min/max constraints should also trigger in the second pass - // resulting in the - // item's size calculation being identical in the first and second - // passes. - deltaFreeSpace -= boundMainSize - childFlexBasis; - deltaFlexGrowFactors -= flexGrowFactor; - } - } - } - - currentRelativeChild = currentRelativeChild->nextChild; - } - - totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; - totalFlexGrowFactors += deltaFlexGrowFactors; - remainingFreeSpace += deltaFreeSpace; - - // Second pass: resolve the sizes of the flexible items - deltaFreeSpace = 0; - currentRelativeChild = firstRelativeChild; - while (currentRelativeChild != NULL) { - childFlexBasis = - fminf(YGResolveValue(¤tRelativeChild->style.maxDimensions[dim[mainAxis]], - mainAxisParentSize), - fmaxf(YGResolveValue(¤tRelativeChild->style.minDimensions[dim[mainAxis]], - mainAxisParentSize), - currentRelativeChild->layout.computedFlexBasis)); - float updatedMainSize = childFlexBasis; - - if (remainingFreeSpace < 0) { - flexShrinkScaledFactor = -YGNodeResolveFlexShrink(currentRelativeChild) * childFlexBasis; - // Is this child able to shrink? - if (flexShrinkScaledFactor != 0) { - float childSize; - - if (totalFlexShrinkScaledFactors == 0) { - childSize = childFlexBasis + flexShrinkScaledFactor; - } else { - childSize = - childFlexBasis + - (remainingFreeSpace / totalFlexShrinkScaledFactors) * flexShrinkScaledFactor; - } - - updatedMainSize = YGNodeBoundAxis(currentRelativeChild, - mainAxis, - childSize, - availableInnerMainDim, - availableInnerWidth); - } - } else if (remainingFreeSpace > 0) { - flexGrowFactor = YGResolveFlexGrow(currentRelativeChild); - - // Is this child able to grow? - if (flexGrowFactor != 0) { - updatedMainSize = - YGNodeBoundAxis(currentRelativeChild, - mainAxis, - childFlexBasis + - remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor, - availableInnerMainDim, - availableInnerWidth); - } - } - - deltaFreeSpace -= updatedMainSize - childFlexBasis; - - const float marginMain = - YGNodeMarginForAxis(currentRelativeChild, mainAxis, availableInnerWidth); - const float marginCross = - YGNodeMarginForAxis(currentRelativeChild, crossAxis, availableInnerWidth); - - float childCrossSize; - float childMainSize = updatedMainSize + marginMain; - YGMeasureMode childCrossMeasureMode; - YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; - - if (!YGFloatIsUndefined(availableInnerCrossDim) && - !YGNodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) && - measureModeCrossDim == YGMeasureModeExactly && - !(isNodeFlexWrap && flexBasisOverflows) && - YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch) { - childCrossSize = availableInnerCrossDim; - childCrossMeasureMode = YGMeasureModeExactly; - } else if (!YGNodeIsStyleDimDefined(currentRelativeChild, - crossAxis, - availableInnerCrossDim)) { - childCrossSize = availableInnerCrossDim; - childCrossMeasureMode = - YGFloatIsUndefined(childCrossSize) ? YGMeasureModeUndefined : YGMeasureModeAtMost; - } else { - childCrossSize = YGResolveValue(currentRelativeChild->resolvedDimensions[dim[crossAxis]], - availableInnerCrossDim) + - marginCross; - const bool isLoosePercentageMeasurement = - currentRelativeChild->resolvedDimensions[dim[crossAxis]]->unit == YGUnitPercent && - measureModeCrossDim != YGMeasureModeExactly; - childCrossMeasureMode = YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement - ? YGMeasureModeUndefined - : YGMeasureModeExactly; - } - - if (!YGFloatIsUndefined(currentRelativeChild->style.aspectRatio)) { - childCrossSize = fmaxf( - isMainAxisRow - ? (childMainSize - marginMain) / currentRelativeChild->style.aspectRatio - : (childMainSize - marginMain) * currentRelativeChild->style.aspectRatio, - YGNodePaddingAndBorderForAxis(currentRelativeChild, crossAxis, availableInnerWidth)); - childCrossMeasureMode = YGMeasureModeExactly; - - // Parent size constraint should have higher priority than flex - if (YGNodeIsFlex(currentRelativeChild)) { - childCrossSize = fminf(childCrossSize - marginCross, availableInnerCrossDim); - childMainSize = - marginMain + (isMainAxisRow - ? childCrossSize * currentRelativeChild->style.aspectRatio - : childCrossSize / currentRelativeChild->style.aspectRatio); - } - - childCrossSize += marginCross; - } - - YGConstrainMaxSizeForMode(currentRelativeChild, - mainAxis, - availableInnerMainDim, - availableInnerWidth, - &childMainMeasureMode, - &childMainSize); - YGConstrainMaxSizeForMode(currentRelativeChild, - crossAxis, - availableInnerCrossDim, - availableInnerWidth, - &childCrossMeasureMode, - &childCrossSize); - - const bool requiresStretchLayout = - !YGNodeIsStyleDimDefined(currentRelativeChild, crossAxis, availableInnerCrossDim) && - YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch; - - const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; - const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; - - const YGMeasureMode childWidthMeasureMode = - isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; - const YGMeasureMode childHeightMeasureMode = - !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; - - // Recursively call the layout algorithm for this child with the updated - // main size. - YGLayoutNodeInternal(currentRelativeChild, - childWidth, - childHeight, - direction, - childWidthMeasureMode, - childHeightMeasureMode, - availableInnerWidth, - availableInnerHeight, - performLayout && !requiresStretchLayout, - "flex", - config); - node->layout.hadOverflow |= currentRelativeChild->layout.hadOverflow; - - currentRelativeChild = currentRelativeChild->nextChild; - } - } - - remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; - node->layout.hadOverflow |= (remainingFreeSpace < 0); - - // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION - - // At this point, all the children have their dimensions set in the main - // axis. - // Their dimensions are also set in the cross axis with the exception of - // items - // that are aligned "stretch". We need to compute these stretch values and - // set the final positions. - - // If we are using "at most" rules in the main axis. Calculate the remaining space when - // constraint by the min size defined for the main axis. - - if (measureModeMainDim == YGMeasureModeAtMost && remainingFreeSpace > 0) { - if (node->style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined && - YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) >= 0) { - remainingFreeSpace = - fmaxf(0, - YGResolveValue(&node->style.minDimensions[dim[mainAxis]], mainAxisParentSize) - - (availableInnerMainDim - remainingFreeSpace)); - } else { - remainingFreeSpace = 0; - } - } - - int numberOfAutoMarginsOnCurrentLine = 0; - for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.positionType == YGPositionTypeRelative) { - if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) { - numberOfAutoMarginsOnCurrentLine++; - } - if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) { - numberOfAutoMarginsOnCurrentLine++; - } - } - } - - if (numberOfAutoMarginsOnCurrentLine == 0) { - switch (justifyContent) { - case YGJustifyCenter: - leadingMainDim = remainingFreeSpace / 2; - break; - case YGJustifyFlexEnd: - leadingMainDim = remainingFreeSpace; - break; - case YGJustifySpaceBetween: - if (itemsOnLine > 1) { - betweenMainDim = fmaxf(remainingFreeSpace, 0) / (itemsOnLine - 1); - } else { - betweenMainDim = 0; - } - break; - case YGJustifySpaceAround: - // Space on the edges is half of the space between elements - betweenMainDim = remainingFreeSpace / itemsOnLine; - leadingMainDim = betweenMainDim / 2; - break; - case YGJustifyFlexStart: - break; - } - } - - float mainDim = leadingPaddingAndBorderMain + leadingMainDim; - float crossDim = 0; - - for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.display == YGDisplayNone) { - continue; - } - if (child->style.positionType == YGPositionTypeAbsolute && - YGNodeIsLeadingPosDefined(child, mainAxis)) { - if (performLayout) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child->layout.position[pos[mainAxis]] = - YGNodeLeadingPosition(child, mainAxis, availableInnerMainDim) + - YGNodeLeadingBorder(node, mainAxis) + - YGNodeLeadingMargin(child, mainAxis, availableInnerWidth); - } - } else { - // Now that we placed the element, we need to update the variables. - // We need to do that only for relative elements. Absolute elements - // do not take part in that phase. - if (child->style.positionType == YGPositionTypeRelative) { - if (YGMarginLeadingValue(child, mainAxis)->unit == YGUnitAuto) { - mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine; - } - - if (performLayout) { - child->layout.position[pos[mainAxis]] += mainDim; - } - - if (YGMarginTrailingValue(child, mainAxis)->unit == YGUnitAuto) { - mainDim += remainingFreeSpace / numberOfAutoMarginsOnCurrentLine; - } - - if (canSkipFlex) { - // If we skipped the flex step, then we can't rely on the - // measuredDims because - // they weren't computed. This means we can't call YGNodeDimWithMargin. - mainDim += betweenMainDim + YGNodeMarginForAxis(child, mainAxis, availableInnerWidth) + - child->layout.computedFlexBasis; - crossDim = availableInnerCrossDim; - } else { - // The main dimension is the sum of all the elements dimension plus the spacing. - mainDim += betweenMainDim + YGNodeDimWithMargin(child, mainAxis, availableInnerWidth); - - // The cross dimension is the max of the elements dimension since - // there can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); - } - } else if (performLayout) { - child->layout.position[pos[mainAxis]] += - YGNodeLeadingBorder(node, mainAxis) + leadingMainDim; - } - } - } - - mainDim += trailingPaddingAndBorderMain; - - float containerCrossAxis = availableInnerCrossDim; - if (measureModeCrossDim == YGMeasureModeUndefined || - measureModeCrossDim == YGMeasureModeAtMost) { - // Compute the cross axis from the max cross dimension of the children. - containerCrossAxis = YGNodeBoundAxis(node, - crossAxis, - crossDim + paddingAndBorderAxisCross, - crossAxisParentSize, - parentWidth) - - paddingAndBorderAxisCross; - } - - // If there's no flex wrap, the cross dimension is defined by the container. - if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) { - crossDim = availableInnerCrossDim; - } - - // Clamp to the min/max size specified on the container. - crossDim = YGNodeBoundAxis(node, - crossAxis, - crossDim + paddingAndBorderAxisCross, - crossAxisParentSize, - parentWidth) - - paddingAndBorderAxisCross; - - // STEP 7: CROSS-AXIS ALIGNMENT - // We can skip child alignment if we're just measuring the container. - if (performLayout) { - for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.display == YGDisplayNone) { - continue; - } - if (child->style.positionType == YGPositionTypeAbsolute) { - // If the child is absolutely positioned and has a - // top/left/bottom/right - // set, override all the previously computed positions to set it - // correctly. - if (YGNodeIsLeadingPosDefined(child, crossAxis)) { - child->layout.position[pos[crossAxis]] = - YGNodeLeadingPosition(child, crossAxis, availableInnerCrossDim) + - YGNodeLeadingBorder(node, crossAxis) + - YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); - } else { - child->layout.position[pos[crossAxis]] = - YGNodeLeadingBorder(node, crossAxis) + - YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); - } - } else { - float leadingCrossDim = leadingPaddingAndBorderCross; - - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross - // axis - const YGAlign alignItem = YGNodeAlignItem(node, child); - - // If the child uses align stretch, we need to lay it out one more - // time, this time - // forcing the cross-axis size to be the computed cross size for the - // current line. - if (alignItem == YGAlignStretch && - YGMarginLeadingValue(child, crossAxis)->unit != YGUnitAuto && - YGMarginTrailingValue(child, crossAxis)->unit != YGUnitAuto) { - // If the child defines a definite size for its cross axis, there's - // no need to stretch. - if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { - float childMainSize = child->layout.measuredDimensions[dim[mainAxis]]; - float childCrossSize = - !YGFloatIsUndefined(child->style.aspectRatio) - ? ((YGNodeMarginForAxis(child, crossAxis, availableInnerWidth) + - (isMainAxisRow ? childMainSize / child->style.aspectRatio - : childMainSize * child->style.aspectRatio))) - : crossDim; - - childMainSize += YGNodeMarginForAxis(child, mainAxis, availableInnerWidth); - - YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; - YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly; - YGConstrainMaxSizeForMode(child, - mainAxis, - availableInnerMainDim, - availableInnerWidth, - &childMainMeasureMode, - &childMainSize); - YGConstrainMaxSizeForMode(child, - crossAxis, - availableInnerCrossDim, - availableInnerWidth, - &childCrossMeasureMode, - &childCrossSize); - - const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; - const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; - - const YGMeasureMode childWidthMeasureMode = - YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly; - const YGMeasureMode childHeightMeasureMode = - YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly; - - YGLayoutNodeInternal(child, - childWidth, - childHeight, - direction, - childWidthMeasureMode, - childHeightMeasureMode, - availableInnerWidth, - availableInnerHeight, - true, - "stretch", - config); - } - } else { - const float remainingCrossDim = - containerCrossAxis - YGNodeDimWithMargin(child, crossAxis, availableInnerWidth); - - if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto && - YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { - leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2); - } else if (YGMarginTrailingValue(child, crossAxis)->unit == YGUnitAuto) { - // No-Op - } else if (YGMarginLeadingValue(child, crossAxis)->unit == YGUnitAuto) { - leadingCrossDim += fmaxf(0.0f, remainingCrossDim); - } else if (alignItem == YGAlignFlexStart) { - // No-Op - } else if (alignItem == YGAlignCenter) { - leadingCrossDim += remainingCrossDim / 2; - } else { - leadingCrossDim += remainingCrossDim; - } - } - // And we apply the position - child->layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; - } - } - } - - totalLineCrossDim += crossDim; - maxLineMainDim = fmaxf(maxLineMainDim, mainDim); - } - - // STEP 8: MULTI-LINE CONTENT ALIGNMENT - if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) && - !YGFloatIsUndefined(availableInnerCrossDim)) { - const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; - - float crossDimLead = 0; - float currentLead = leadingPaddingAndBorderCross; - - switch (node->style.alignContent) { - case YGAlignFlexEnd: - currentLead += remainingAlignContentDim; - break; - case YGAlignCenter: - currentLead += remainingAlignContentDim / 2; - break; - case YGAlignStretch: - if (availableInnerCrossDim > totalLineCrossDim) { - crossDimLead = remainingAlignContentDim / lineCount; - } - break; - case YGAlignSpaceAround: - if (availableInnerCrossDim > totalLineCrossDim) { - currentLead += remainingAlignContentDim / (2 * lineCount); - if (lineCount > 1) { - crossDimLead = remainingAlignContentDim / lineCount; - } - } else { - currentLead += remainingAlignContentDim / 2; - } - break; - case YGAlignSpaceBetween: - if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) { - crossDimLead = remainingAlignContentDim / (lineCount - 1); - } - break; - case YGAlignAuto: - case YGAlignFlexStart: - case YGAlignBaseline: - break; - } - - uint32_t endIndex = 0; - for (uint32_t i = 0; i < lineCount; i++) { - const uint32_t startIndex = endIndex; - uint32_t ii; - - // compute the line's height and find the endIndex - float lineHeight = 0; - float maxAscentForCurrentLine = 0; - float maxDescentForCurrentLine = 0; - for (ii = startIndex; ii < childCount; ii++) { - const YGNodeRef child = YGNodeListGet(node->children, ii); - if (child->style.display == YGDisplayNone) { - continue; - } - if (child->style.positionType == YGPositionTypeRelative) { - if (child->lineIndex != i) { - break; - } - if (YGNodeIsLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf(lineHeight, - child->layout.measuredDimensions[dim[crossAxis]] + - YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)); - } - if (YGNodeAlignItem(node, child) == YGAlignBaseline) { - const float ascent = - YGBaseline(child) + - YGNodeLeadingMargin(child, YGFlexDirectionColumn, availableInnerWidth); - const float descent = - child->layout.measuredDimensions[YGDimensionHeight] + - YGNodeMarginForAxis(child, YGFlexDirectionColumn, availableInnerWidth) - ascent; - maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent); - maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent); - lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); - } - } - } - endIndex = ii; - lineHeight += crossDimLead; - - if (performLayout) { - for (ii = startIndex; ii < endIndex; ii++) { - const YGNodeRef child = YGNodeListGet(node->children, ii); - if (child->style.display == YGDisplayNone) { - continue; - } - if (child->style.positionType == YGPositionTypeRelative) { - switch (YGNodeAlignItem(node, child)) { - case YGAlignFlexStart: { - child->layout.position[pos[crossAxis]] = - currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); - break; - } - case YGAlignFlexEnd: { - child->layout.position[pos[crossAxis]] = - currentLead + lineHeight - - YGNodeTrailingMargin(child, crossAxis, availableInnerWidth) - - child->layout.measuredDimensions[dim[crossAxis]]; - break; - } - case YGAlignCenter: { - float childHeight = child->layout.measuredDimensions[dim[crossAxis]]; - child->layout.position[pos[crossAxis]] = - currentLead + (lineHeight - childHeight) / 2; - break; - } - case YGAlignStretch: { - child->layout.position[pos[crossAxis]] = - currentLead + YGNodeLeadingMargin(child, crossAxis, availableInnerWidth); - - // Remeasure child with the line height as it as been only measured with the - // parents height yet. - if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { - const float childWidth = - isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionWidth] + - YGNodeMarginForAxis(child, mainAxis, availableInnerWidth)) - : lineHeight; - - const float childHeight = - !isMainAxisRow ? (child->layout.measuredDimensions[YGDimensionHeight] + - YGNodeMarginForAxis(child, crossAxis, availableInnerWidth)) - : lineHeight; - - if (!(YGFloatsEqual(childWidth, - child->layout.measuredDimensions[YGDimensionWidth]) && - YGFloatsEqual(childHeight, - child->layout.measuredDimensions[YGDimensionHeight]))) { - YGLayoutNodeInternal(child, - childWidth, - childHeight, - direction, - YGMeasureModeExactly, - YGMeasureModeExactly, - availableInnerWidth, - availableInnerHeight, - true, - "multiline-stretch", - config); - } - } - break; - } - case YGAlignBaseline: { - child->layout.position[YGEdgeTop] = - currentLead + maxAscentForCurrentLine - YGBaseline(child) + - YGNodeLeadingPosition(child, YGFlexDirectionColumn, availableInnerCrossDim); - break; - } - case YGAlignAuto: - case YGAlignSpaceBetween: - case YGAlignSpaceAround: - break; - } - } - } - } - - currentLead += lineHeight; - } - } - - // STEP 9: COMPUTING FINAL DIMENSIONS - node->layout.measuredDimensions[YGDimensionWidth] = YGNodeBoundAxis( - node, YGFlexDirectionRow, availableWidth - marginAxisRow, parentWidth, parentWidth); - node->layout.measuredDimensions[YGDimensionHeight] = YGNodeBoundAxis( - node, YGFlexDirectionColumn, availableHeight - marginAxisColumn, parentHeight, parentWidth); - - // If the user didn't specify a width or height for the node, set the - // dimensions based on the children. - if (measureModeMainDim == YGMeasureModeUndefined || - (node->style.overflow != YGOverflowScroll && measureModeMainDim == YGMeasureModeAtMost)) { - // Clamp the size to the min/max size, if specified, and make sure it - // doesn't go below the padding and border amount. - node->layout.measuredDimensions[dim[mainAxis]] = - YGNodeBoundAxis(node, mainAxis, maxLineMainDim, mainAxisParentSize, parentWidth); - } else if (measureModeMainDim == YGMeasureModeAtMost && - node->style.overflow == YGOverflowScroll) { - node->layout.measuredDimensions[dim[mainAxis]] = fmaxf( - fminf(availableInnerMainDim + paddingAndBorderAxisMain, - YGNodeBoundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim, mainAxisParentSize)), - paddingAndBorderAxisMain); - } - - if (measureModeCrossDim == YGMeasureModeUndefined || - (node->style.overflow != YGOverflowScroll && measureModeCrossDim == YGMeasureModeAtMost)) { - // Clamp the size to the min/max size, if specified, and make sure it - // doesn't go below the padding and border amount. - node->layout.measuredDimensions[dim[crossAxis]] = - YGNodeBoundAxis(node, - crossAxis, - totalLineCrossDim + paddingAndBorderAxisCross, - crossAxisParentSize, - parentWidth); - } else if (measureModeCrossDim == YGMeasureModeAtMost && - node->style.overflow == YGOverflowScroll) { - node->layout.measuredDimensions[dim[crossAxis]] = - fmaxf(fminf(availableInnerCrossDim + paddingAndBorderAxisCross, - YGNodeBoundAxisWithinMinAndMax(node, - crossAxis, - totalLineCrossDim + paddingAndBorderAxisCross, - crossAxisParentSize)), - paddingAndBorderAxisCross); - } - - // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse. - if (performLayout && node->style.flexWrap == YGWrapWrapReverse) { - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeGetChild(node, i); - if (child->style.positionType == YGPositionTypeRelative) { - child->layout.position[pos[crossAxis]] = node->layout.measuredDimensions[dim[crossAxis]] - - child->layout.position[pos[crossAxis]] - - child->layout.measuredDimensions[dim[crossAxis]]; - } - } - } - - if (performLayout) { - // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN - for (currentAbsoluteChild = firstAbsoluteChild; currentAbsoluteChild != NULL; - currentAbsoluteChild = currentAbsoluteChild->nextChild) { - YGNodeAbsoluteLayoutChild(node, - currentAbsoluteChild, - availableInnerWidth, - isMainAxisRow ? measureModeMainDim : measureModeCrossDim, - availableInnerHeight, - direction, - config); - } - - // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN - const bool needsMainTrailingPos = - mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse; - const bool needsCrossTrailingPos = - crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse; - - // Set trailing position if necessary. - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (uint32_t i = 0; i < childCount; i++) { - const YGNodeRef child = YGNodeListGet(node->children, i); - if (child->style.display == YGDisplayNone) { - continue; - } - if (needsMainTrailingPos) { - YGNodeSetChildTrailingPosition(node, child, mainAxis); - } - - if (needsCrossTrailingPos) { - YGNodeSetChildTrailingPosition(node, child, crossAxis); - } - } - } - } -} - -uint32_t gDepth = 0; -bool gPrintTree = false; -bool gPrintChanges = false; -bool gPrintSkips = false; - -static const char *spacer = " "; - -static const char *YGSpacer(const unsigned long level) { - const size_t spacerLen = strlen(spacer); - if (level > spacerLen) { - return &spacer[0]; - } else { - return &spacer[spacerLen - level]; - } -} - -static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) { - const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"}; - const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED", - "LAY_EXACTLY", - "LAY_AT_" - "MOST"}; - - if (mode >= YGMeasureModeCount) { - return ""; - } - - return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode]; -} - -static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode, - float size, - float lastComputedSize) { - return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize); -} - -static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode, - float size, - YGMeasureMode lastSizeMode, - float lastComputedSize) { - return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined && - (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize)); -} - -static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode, - float size, - YGMeasureMode lastSizeMode, - float lastSize, - float lastComputedSize) { - return lastSizeMode == YGMeasureModeAtMost && sizeMode == YGMeasureModeAtMost && - lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); -} - -static float YGRoundValueToPixelGrid(const float value, - const float pointScaleFactor, - const bool forceCeil, - const bool forceFloor) { - float scaledValue = value * pointScaleFactor; - float fractial = fmodf(scaledValue, 1.0); - if (YGFloatsEqual(fractial, 0)) { - // Still remove fractial as fractial could be extremely small. - scaledValue = scaledValue - fractial; - } else if (forceCeil) { - scaledValue = scaledValue - fractial + 1.0; - } else if (forceFloor) { - scaledValue = scaledValue - fractial; - } else { - scaledValue = scaledValue - fractial + (fractial >= 0.5f ? 1.0 : 0); - } - return scaledValue / pointScaleFactor; -} - -bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, - const float width, - const YGMeasureMode heightMode, - const float height, - const YGMeasureMode lastWidthMode, - const float lastWidth, - const YGMeasureMode lastHeightMode, - const float lastHeight, - const float lastComputedWidth, - const float lastComputedHeight, - const float marginRow, - const float marginColumn, - const YGConfigRef config) { - if (lastComputedHeight < 0 || lastComputedWidth < 0) { - return false; - } - bool useRoundedComparison = config != NULL && config->pointScaleFactor != 0; - const float effectiveWidth = useRoundedComparison ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false) : width; - const float effectiveHeight = useRoundedComparison ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false) : height; - const float effectiveLastWidth = useRoundedComparison ? YGRoundValueToPixelGrid(lastWidth, config->pointScaleFactor, false, false) : lastWidth; - const float effectiveLastHeight = useRoundedComparison ? YGRoundValueToPixelGrid(lastHeight, config->pointScaleFactor, false, false) : lastHeight; - - const bool hasSameWidthSpec = lastWidthMode == widthMode && YGFloatsEqual(effectiveLastWidth, effectiveWidth); - const bool hasSameHeightSpec = lastHeightMode == heightMode && YGFloatsEqual(effectiveLastHeight, effectiveHeight); - - const bool widthIsCompatible = - hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode, - width - marginRow, - lastComputedWidth) || - YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode, - width - marginRow, - lastWidthMode, - lastComputedWidth) || - YGMeasureModeNewMeasureSizeIsStricterAndStillValid( - widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth); - - const bool heightIsCompatible = - hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode, - height - marginColumn, - lastComputedHeight) || - YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode, - height - marginColumn, - lastHeightMode, - lastComputedHeight) || - YGMeasureModeNewMeasureSizeIsStricterAndStillValid( - heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight); - - return widthIsCompatible && heightIsCompatible; -} - -// -// This is a wrapper around the YGNodelayoutImpl function. It determines -// whether the layout request is redundant and can be skipped. -// -// Parameters: -// Input parameters are the same as YGNodelayoutImpl (see above) -// Return parameter is true if layout was performed, false if skipped -// -bool YGLayoutNodeInternal(const YGNodeRef node, - const float availableWidth, - const float availableHeight, - const YGDirection parentDirection, - const YGMeasureMode widthMeasureMode, - const YGMeasureMode heightMeasureMode, - const float parentWidth, - const float parentHeight, - const bool performLayout, - const char *reason, - const YGConfigRef config) { - YGLayout *layout = &node->layout; - - gDepth++; - - const bool needToVisitNode = - (node->isDirty && layout->generationCount != gCurrentGenerationCount) || - layout->lastParentDirection != parentDirection; - - if (needToVisitNode) { - // Invalidate the cached results. - layout->nextCachedMeasurementsIndex = 0; - layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1; - layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1; - layout->cachedLayout.computedWidth = -1; - layout->cachedLayout.computedHeight = -1; - } - - YGCachedMeasurement *cachedResults = NULL; - - // Determine whether the results are already cached. We maintain a separate - // cache for layouts and measurements. A layout operation modifies the - // positions - // and dimensions for nodes in the subtree. The algorithm assumes that each - // node - // gets layed out a maximum of one time per tree layout, but multiple - // measurements - // may be required to resolve all of the flex dimensions. - // We handle nodes with measure functions specially here because they are the - // most - // expensive to measure, so it's worth avoiding redundant measurements if at - // all possible. - if (node->measure) { - const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); - const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); - - // First, try to use the layout cache. - if (YGNodeCanUseCachedMeasurement(widthMeasureMode, - availableWidth, - heightMeasureMode, - availableHeight, - layout->cachedLayout.widthMeasureMode, - layout->cachedLayout.availableWidth, - layout->cachedLayout.heightMeasureMode, - layout->cachedLayout.availableHeight, - layout->cachedLayout.computedWidth, - layout->cachedLayout.computedHeight, - marginAxisRow, - marginAxisColumn, - config)) { - cachedResults = &layout->cachedLayout; - } else { - // Try to use the measurement cache. - for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { - if (YGNodeCanUseCachedMeasurement(widthMeasureMode, - availableWidth, - heightMeasureMode, - availableHeight, - layout->cachedMeasurements[i].widthMeasureMode, - layout->cachedMeasurements[i].availableWidth, - layout->cachedMeasurements[i].heightMeasureMode, - layout->cachedMeasurements[i].availableHeight, - layout->cachedMeasurements[i].computedWidth, - layout->cachedMeasurements[i].computedHeight, - marginAxisRow, - marginAxisColumn, - config)) { - cachedResults = &layout->cachedMeasurements[i]; - break; - } - } - } - } else if (performLayout) { - if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) && - YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) && - layout->cachedLayout.widthMeasureMode == widthMeasureMode && - layout->cachedLayout.heightMeasureMode == heightMeasureMode) { - cachedResults = &layout->cachedLayout; - } - } else { - for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { - if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) && - YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) && - layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode && - layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) { - cachedResults = &layout->cachedMeasurements[i]; - break; - } - } - } - - if (!needToVisitNode && cachedResults != NULL) { - layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth; - layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight; - - if (gPrintChanges && gPrintSkips) { - printf("%s%d.{[skipped] ", YGSpacer(gDepth), gDepth); - if (node->print) { - node->print(node); - } - printf("wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", - YGMeasureModeName(widthMeasureMode, performLayout), - YGMeasureModeName(heightMeasureMode, performLayout), - availableWidth, - availableHeight, - cachedResults->computedWidth, - cachedResults->computedHeight, - reason); - } - } else { - if (gPrintChanges) { - printf("%s%d.{%s", YGSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); - if (node->print) { - node->print(node); - } - printf("wm: %s, hm: %s, aw: %f ah: %f %s\n", - YGMeasureModeName(widthMeasureMode, performLayout), - YGMeasureModeName(heightMeasureMode, performLayout), - availableWidth, - availableHeight, - reason); - } - - YGNodelayoutImpl(node, - availableWidth, - availableHeight, - parentDirection, - widthMeasureMode, - heightMeasureMode, - parentWidth, - parentHeight, - performLayout, - config); - - if (gPrintChanges) { - printf("%s%d.}%s", YGSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); - if (node->print) { - node->print(node); - } - printf("wm: %s, hm: %s, d: (%f, %f) %s\n", - YGMeasureModeName(widthMeasureMode, performLayout), - YGMeasureModeName(heightMeasureMode, performLayout), - layout->measuredDimensions[YGDimensionWidth], - layout->measuredDimensions[YGDimensionHeight], - reason); - } - - layout->lastParentDirection = parentDirection; - - if (cachedResults == NULL) { - if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) { - if (gPrintChanges) { - printf("Out of cache entries!\n"); - } - layout->nextCachedMeasurementsIndex = 0; - } - - YGCachedMeasurement *newCacheEntry; - if (performLayout) { - // Use the single layout cache entry. - newCacheEntry = &layout->cachedLayout; - } else { - // Allocate a new measurement cache entry. - newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex]; - layout->nextCachedMeasurementsIndex++; - } - - newCacheEntry->availableWidth = availableWidth; - newCacheEntry->availableHeight = availableHeight; - newCacheEntry->widthMeasureMode = widthMeasureMode; - newCacheEntry->heightMeasureMode = heightMeasureMode; - newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth]; - newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight]; - } - } - - if (performLayout) { - node->layout.dimensions[YGDimensionWidth] = node->layout.measuredDimensions[YGDimensionWidth]; - node->layout.dimensions[YGDimensionHeight] = node->layout.measuredDimensions[YGDimensionHeight]; - node->hasNewLayout = true; - node->isDirty = false; - } - - gDepth--; - layout->generationCount = gCurrentGenerationCount; - return (needToVisitNode || cachedResults == NULL); -} - -void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) { - YGAssertWithConfig(config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero"); - - // We store points for Pixel as we will use it for rounding - if (pixelsInPoint == 0.0f) { - // Zero is used to skip rounding - config->pointScaleFactor = 0.0f; - } else { - config->pointScaleFactor = pixelsInPoint; - } -} - -static void YGRoundToPixelGrid(const YGNodeRef node, - const float pointScaleFactor, - const float absoluteLeft, - const float absoluteTop) { - if (pointScaleFactor == 0.0f) { - return; - } - - const float nodeLeft = node->layout.position[YGEdgeLeft]; - const float nodeTop = node->layout.position[YGEdgeTop]; - - const float nodeWidth = node->layout.dimensions[YGDimensionWidth]; - const float nodeHeight = node->layout.dimensions[YGDimensionHeight]; - - const float absoluteNodeLeft = absoluteLeft + nodeLeft; - const float absoluteNodeTop = absoluteTop + nodeTop; - - const float absoluteNodeRight = absoluteNodeLeft + nodeWidth; - const float absoluteNodeBottom = absoluteNodeTop + nodeHeight; - - // If a node has a custom measure function we never want to round down its size as this could - // lead to unwanted text truncation. - const bool textRounding = node->nodeType == YGNodeTypeText; - - node->layout.position[YGEdgeLeft] = - YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding); - node->layout.position[YGEdgeTop] = - YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding); - - // We multiply dimension by scale factor and if the result is close to the whole number, we don't have any fraction - // To verify if the result is close to whole number we want to check both floor and ceil numbers - const bool hasFractionalWidth = !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) && - !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0); - const bool hasFractionalHeight = !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) && - !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0); - - node->layout.dimensions[YGDimensionWidth] = - YGRoundValueToPixelGrid( - absoluteNodeRight, - pointScaleFactor, - (textRounding && hasFractionalWidth), - (textRounding && !hasFractionalWidth)) - - YGRoundValueToPixelGrid(absoluteNodeLeft, pointScaleFactor, false, textRounding); - node->layout.dimensions[YGDimensionHeight] = - YGRoundValueToPixelGrid( - absoluteNodeBottom, - pointScaleFactor, - (textRounding && hasFractionalHeight), - (textRounding && !hasFractionalHeight)) - - YGRoundValueToPixelGrid(absoluteNodeTop, pointScaleFactor, false, textRounding); - - const uint32_t childCount = YGNodeListCount(node->children); - for (uint32_t i = 0; i < childCount; i++) { - YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop); - } -} - -void YGNodeCalculateLayout(const YGNodeRef node, - const float parentWidth, - const float parentHeight, - const YGDirection parentDirection) { - // Increment the generation count. This will force the recursive routine to - // visit - // all dirty nodes at least once. Subsequent visits will be skipped if the - // input - // parameters don't change. - gCurrentGenerationCount++; - - YGResolveDimensions(node); - - float width = YGUndefined; - YGMeasureMode widthMeasureMode = YGMeasureModeUndefined; - if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, parentWidth)) { - width = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionRow]], parentWidth) + - YGNodeMarginForAxis(node, YGFlexDirectionRow, parentWidth); - widthMeasureMode = YGMeasureModeExactly; - } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth) >= 0.0f) { - width = YGResolveValue(&node->style.maxDimensions[YGDimensionWidth], parentWidth); - widthMeasureMode = YGMeasureModeAtMost; - } else { - width = parentWidth; - widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined : YGMeasureModeExactly; - } - - float height = YGUndefined; - YGMeasureMode heightMeasureMode = YGMeasureModeUndefined; - if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, parentHeight)) { - height = YGResolveValue(node->resolvedDimensions[dim[YGFlexDirectionColumn]], parentHeight) + - YGNodeMarginForAxis(node, YGFlexDirectionColumn, parentWidth); - heightMeasureMode = YGMeasureModeExactly; - } else if (YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight) >= 0.0f) { - height = YGResolveValue(&node->style.maxDimensions[YGDimensionHeight], parentHeight); - heightMeasureMode = YGMeasureModeAtMost; - } else { - height = parentHeight; - heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined : YGMeasureModeExactly; - } - - if (YGLayoutNodeInternal(node, - width, - height, - parentDirection, - widthMeasureMode, - heightMeasureMode, - parentWidth, - parentHeight, - true, - "initial", - node->config)) { - YGNodeSetPosition(node, node->layout.direction, parentWidth, parentHeight, parentWidth); - YGRoundToPixelGrid(node, node->config->pointScaleFactor, 0.0f, 0.0f); - - if (gPrintTree) { - YGNodePrint(node, YGPrintOptionsLayout | YGPrintOptionsChildren | YGPrintOptionsStyle); - } - } -} - -void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { - if (logger != NULL) { - config->logger = logger; - } else { -#ifdef ANDROID - config->logger = &YGAndroidLog; -#else - config->logger = &YGDefaultLog; -#endif - } -} - -static void YGVLog(const YGConfigRef config, - const YGNodeRef node, - YGLogLevel level, - const char *format, - va_list args) { - const YGConfigRef logConfig = config != NULL ? config : &gYGConfigDefaults; - logConfig->logger(logConfig, node, level, format, args); - - if (level == YGLogLevelFatal) { - abort(); - } -} - -void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...) { - va_list args; - va_start(args, format); - YGVLog(config, NULL, level, format, args); - va_end(args); -} - -void YGLog(const YGNodeRef node, YGLogLevel level, const char *format, ...) { - va_list args; - va_start(args, format); - YGVLog(node == NULL ? NULL : node->config, node, level, format, args); - va_end(args); -} - -void YGAssert(const bool condition, const char *message) { - if (!condition) { - YGLog(NULL, YGLogLevelFatal, "%s\n", message); - } -} - -void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message) { - if (!condition) { - YGLog(node, YGLogLevelFatal, "%s\n", message); - } -} - -void YGAssertWithConfig(const YGConfigRef config, const bool condition, const char *message) { - if (!condition) { - YGLogWithConfig(config, YGLogLevelFatal, "%s\n", message); - } -} - -void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, - const YGExperimentalFeature feature, - const bool enabled) { - config->experimentalFeatures[feature] = enabled; -} - -inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, - const YGExperimentalFeature feature) { - return config->experimentalFeatures[feature]; -} - -void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) { - config->useWebDefaults = enabled; -} - -void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config, - const bool useLegacyStretchBehaviour) { - config->useLegacyStretchBehaviour = useLegacyStretchBehaviour; -} - -bool YGConfigGetUseWebDefaults(const YGConfigRef config) { - return config->useWebDefaults; -} - -void YGConfigSetContext(const YGConfigRef config, void *context) { - config->context = context; -} - -void *YGConfigGetContext(const YGConfigRef config) { - return config->context; -} - -void YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree) { - YGAssert(gNodeInstanceCount == 0 && gConfigInstanceCount == 0, - "Cannot set memory functions: all node must be freed first"); - YGAssert((ygmalloc == NULL && yccalloc == NULL && ygrealloc == NULL && ygfree == NULL) || - (ygmalloc != NULL && yccalloc != NULL && ygrealloc != NULL && ygfree != NULL), - "Cannot set memory functions: functions must be all NULL or Non-NULL"); - - if (ygmalloc == NULL || yccalloc == NULL || ygrealloc == NULL || ygfree == NULL) { - gYGMalloc = &malloc; - gYGCalloc = &calloc; - gYGRealloc = &realloc; - gYGFree = &free; - } else { - gYGMalloc = ygmalloc; - gYGCalloc = yccalloc; - gYGRealloc = ygrealloc; - gYGFree = ygfree; - } -} diff --git a/Sources/yoga/Yoga.cpp b/Sources/yoga/Yoga.cpp new file mode 100644 index 00000000..ff57e99f --- /dev/null +++ b/Sources/yoga/Yoga.cpp @@ -0,0 +1,3764 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Yoga.h" +#include +#include +#include "Utils.h" +#include "YGNode.h" +#include "YGNodePrint.h" +#include "Yoga-internal.h" + +#ifdef _MSC_VER +#include + +/* define fmaxf if < VC12 */ +#if _MSC_VER < 1800 +__forceinline const float fmaxf(const float a, const float b) { + return (a > b) ? a : b; +} +#endif +#endif + +#ifdef ANDROID +static int YGAndroidLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args); +#else +static int YGDefaultLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args); +#endif + +static YGConfig gYGConfigDefaults = { + .experimentalFeatures = + { + [YGExperimentalFeatureWebFlexBasis] = false, + }, + .useWebDefaults = false, + .useLegacyStretchBehaviour = false, + .shouldDiffLayoutWithoutLegacyStretchBehaviour = false, + .pointScaleFactor = 1.0f, +#ifdef ANDROID + .logger = &YGAndroidLog, +#else + .logger = &YGDefaultLog, +#endif + .cloneNodeCallback = nullptr, + .context = nullptr, +}; + +const YGValue YGValueZero = {.value = 0, .unit = YGUnitPoint}; +const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; +const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; + +#ifdef ANDROID +#include +static int YGAndroidLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + int androidLevel = YGLogLevelDebug; + switch (level) { + case YGLogLevelFatal: + androidLevel = ANDROID_LOG_FATAL; + break; + case YGLogLevelError: + androidLevel = ANDROID_LOG_ERROR; + break; + case YGLogLevelWarn: + androidLevel = ANDROID_LOG_WARN; + break; + case YGLogLevelInfo: + androidLevel = ANDROID_LOG_INFO; + break; + case YGLogLevelDebug: + androidLevel = ANDROID_LOG_DEBUG; + break; + case YGLogLevelVerbose: + androidLevel = ANDROID_LOG_VERBOSE; + break; + } + const int result = __android_log_vprint(androidLevel, "yoga", format, args); + return result; +} +#else +#define YG_UNUSED(x) (void)(x); + +static int YGDefaultLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + YG_UNUSED(config); + YG_UNUSED(node); + switch (level) { + case YGLogLevelError: + case YGLogLevelFatal: + return vfprintf(stderr, format, args); + case YGLogLevelWarn: + case YGLogLevelInfo: + case YGLogLevelDebug: + case YGLogLevelVerbose: + default: + return vprintf(format, args); + } +} + +#undef YG_UNUSED +#endif + +bool YGFloatIsUndefined(const float value) { + return std::isnan(value); +} + +const YGValue* YGComputedEdgeValue( + const std::array& edges, + const YGEdge edge, + const YGValue* const defaultValue) { + if (edges[edge].unit != YGUnitUndefined) { + return &edges[edge]; + } + + if ((edge == YGEdgeTop || edge == YGEdgeBottom) && + edges[YGEdgeVertical].unit != YGUnitUndefined) { + return &edges[YGEdgeVertical]; + } + + if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) && + edges[YGEdgeHorizontal].unit != YGUnitUndefined) { + return &edges[YGEdgeHorizontal]; + } + + if (edges[YGEdgeAll].unit != YGUnitUndefined) { + return &edges[YGEdgeAll]; + } + + if (edge == YGEdgeStart || edge == YGEdgeEnd) { + return &YGValueUndefined; + } + + return defaultValue; +} + +void* YGNodeGetContext(YGNodeRef node) { + return node->getContext(); +} + +void YGNodeSetContext(YGNodeRef node, void* context) { + return node->setContext(context); +} + +YGMeasureFunc YGNodeGetMeasureFunc(YGNodeRef node) { + return node->getMeasure(); +} + +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) { + node->setMeasureFunc(measureFunc); +} + +YGBaselineFunc YGNodeGetBaselineFunc(YGNodeRef node) { + return node->getBaseline(); +} + +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) { + node->setBaseLineFunc(baselineFunc); +} + +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node) { + return node->getDirtied(); +} + +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) { + node->setDirtiedFunc(dirtiedFunc); +} + +YGPrintFunc YGNodeGetPrintFunc(YGNodeRef node) { + return node->getPrintFunc(); +} + +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) { + node->setPrintFunc(printFunc); +} + +bool YGNodeGetHasNewLayout(YGNodeRef node) { + return node->getHasNewLayout(); +} + +void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) { + node->setHasNewLayout(hasNewLayout); +} + +YGNodeType YGNodeGetNodeType(YGNodeRef node) { + return node->getNodeType(); +} + +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) { + return node->setNodeType(nodeType); +} + +bool YGNodeIsDirty(YGNodeRef node) { + return node->isDirty(); +} + +bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) { + return node->didUseLegacyFlag(); +} + +void YGNodeMarkDirtyAndPropogateToDescendants(const YGNodeRef node) { + return node->markDirtyAndPropogateDownwards(); +} + +int32_t gNodeInstanceCount = 0; +int32_t gConfigInstanceCount = 0; + +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { + const YGNodeRef node = new YGNode(); + YGAssertWithConfig( + config, node != nullptr, "Could not allocate memory for node"); + gNodeInstanceCount++; + + if (config->useWebDefaults) { + node->setStyleFlexDirection(YGFlexDirectionRow); + node->setStyleAlignContent(YGAlignStretch); + } + node->setConfig(config); + return node; +} + +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(&gYGConfigDefaults); +} + +YGNodeRef YGNodeClone(YGNodeRef oldNode) { + YGNodeRef node = new YGNode(*oldNode); + YGAssertWithConfig( + oldNode->getConfig(), + node != nullptr, + "Could not allocate memory for node"); + gNodeInstanceCount++; + node->setParent(nullptr); + return node; +} + +static YGConfigRef YGConfigClone(const YGConfig& oldConfig) { + const YGConfigRef config = new YGConfig(oldConfig); + YGAssert(config != nullptr, "Could not allocate memory for config"); + if (config == nullptr) { + abort(); + } + gConfigInstanceCount++; + return config; +} + +static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) { + YGNodeRef node = YGNodeClone(oldNode); + YGVector vec = YGVector(); + vec.reserve(oldNode->getChildren().size()); + YGNodeRef childNode = nullptr; + for (auto& item : oldNode->getChildren()) { + childNode = YGNodeDeepClone(item); + childNode->setParent(node); + vec.push_back(childNode); + } + node->setChildren(vec); + + if (oldNode->getConfig() != nullptr) { + node->setConfig(YGConfigClone(*(oldNode->getConfig()))); + } + + if (oldNode->getNextChild() != nullptr) { + node->setNextChild(YGNodeDeepClone(oldNode->getNextChild())); + } + + return node; +} + +void YGNodeFree(const YGNodeRef node) { + if (node->getParent()) { + node->getParent()->removeChild(node); + node->setParent(nullptr); + } + + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + child->setParent(nullptr); + } + + node->clearChildren(); + delete node; + gNodeInstanceCount--; +} + +static void YGConfigFreeRecursive(const YGNodeRef root) { + if (root->getConfig() != nullptr) { + gConfigInstanceCount--; + delete root->getConfig(); + } + // Delete configs recursively for childrens + for (uint32_t i = 0; i < root->getChildrenCount(); ++i) { + YGConfigFreeRecursive(root->getChild(i)); + } +} + +void YGNodeFreeRecursive(const YGNodeRef root) { + while (YGNodeGetChildCount(root) > 0) { + const YGNodeRef child = YGNodeGetChild(root, 0); + if (child->getParent() != root) { + // Don't free shared nodes that we don't own. + break; + } + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } + YGNodeFree(root); +} + +void YGNodeReset(const YGNodeRef node) { + YGAssertWithNode(node, + YGNodeGetChildCount(node) == 0, + "Cannot reset a node which still has children attached"); + YGAssertWithNode( + node, + node->getParent() == nullptr, + "Cannot reset a node still attached to a parent"); + + node->clearChildren(); + + const YGConfigRef config = node->getConfig(); + *node = YGNode(); + if (config->useWebDefaults) { + node->setStyleFlexDirection(YGFlexDirectionRow); + node->setStyleAlignContent(YGAlignStretch); + } + node->setConfig(config); +} + +int32_t YGNodeGetInstanceCount(void) { + return gNodeInstanceCount; +} + +int32_t YGConfigGetInstanceCount(void) { + return gConfigInstanceCount; +} + +// Export only for C# +YGConfigRef YGConfigGetDefault() { + return &gYGConfigDefaults; +} + +YGConfigRef YGConfigNew(void) { + const YGConfigRef config = (const YGConfigRef)malloc(sizeof(YGConfig)); + YGAssert(config != nullptr, "Could not allocate memory for config"); + if (config == nullptr) { + abort(); + } + gConfigInstanceCount++; + memcpy(config, &gYGConfigDefaults, sizeof(YGConfig)); + return config; +} + +void YGConfigFree(const YGConfigRef config) { + free(config); + gConfigInstanceCount--; +} + +void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) { + memcpy(dest, src, sizeof(YGConfig)); +} + +void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) { + YGAssertWithNode( + node, + child->getParent() == nullptr, + "Child already has a parent, it must be removed first."); + YGAssertWithNode( + node, + node->getMeasure() == nullptr, + "Cannot add child: Nodes with measure functions cannot have children."); + + node->cloneChildrenIfNeeded(); + node->insertChild(child, index); + child->setParent(node); + node->markDirtyAndPropogate(); +} + +void YGNodeRemoveChild(const YGNodeRef parent, const YGNodeRef excludedChild) { + // This algorithm is a forked variant from cloneChildrenIfNeeded in YGNode + // that excludes a child. + const uint32_t childCount = YGNodeGetChildCount(parent); + + if (childCount == 0) { + // This is an empty set. Nothing to remove. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(parent, 0); + if (firstChild->getParent() == parent) { + // If the first child has this node as its parent, we assume that it is already unique. + // We can now try to delete a child in this list. + if (parent->removeChild(excludedChild)) { + excludedChild->setLayout( + YGNode().getLayout()); // layout is no longer valid + excludedChild->setParent(nullptr); + parent->markDirtyAndPropogate(); + } + return; + } + // Otherwise we have to clone the node list except for the child we're trying to delete. + // We don't want to simply clone all children, because then the host will need to free + // the clone of the child that was just deleted. + const YGNodeClonedFunc cloneNodeCallback = + parent->getConfig()->cloneNodeCallback; + uint32_t nextInsertIndex = 0; + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = parent->getChild(i); + if (excludedChild == oldChild) { + // Ignore the deleted child. Don't reset its layout or parent since it is still valid + // in the other parent. However, since this parent has now changed, we need to mark it + // as dirty. + parent->markDirtyAndPropogate(); + continue; + } + const YGNodeRef newChild = YGNodeClone(oldChild); + parent->replaceChild(newChild, nextInsertIndex); + newChild->setParent(parent); + if (cloneNodeCallback) { + cloneNodeCallback(oldChild, newChild, parent, nextInsertIndex); + } + nextInsertIndex++; + } + while (nextInsertIndex < childCount) { + parent->removeChild(nextInsertIndex); + nextInsertIndex++; + } +} + +void YGNodeRemoveAllChildren(const YGNodeRef parent) { + const uint32_t childCount = YGNodeGetChildCount(parent); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(parent, 0); + if (firstChild->getParent() == parent) { + // If the first child has this node as its parent, we assume that this child set is unique. + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = YGNodeGetChild(parent, i); + oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid + oldChild->setParent(nullptr); + } + parent->clearChildren(); + parent->markDirtyAndPropogate(); + return; + } + // Otherwise, we are not the owner of the child set. We don't have to do anything to clear it. + parent->setChildren(YGVector()); + parent->markDirtyAndPropogate(); +} + +YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) { + if (index < node->getChildren().size()) { + return node->getChild(index); + } + return nullptr; +} + +uint32_t YGNodeGetChildCount(const YGNodeRef node) { + return static_cast(node->getChildren().size()); +} + +YGNodeRef YGNodeGetParent(const YGNodeRef node) { + return node->getParent(); +} + +void YGNodeMarkDirty(const YGNodeRef node) { + YGAssertWithNode( + node, + node->getMeasure() != nullptr, + "Only leaf nodes with custom measure functions" + "should manually mark themselves as dirty"); + + node->markDirtyAndPropogate(); +} + +void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) { + if (!(dstNode->getStyle() == srcNode->getStyle())) { + dstNode->setStyle(srcNode->getStyle()); + dstNode->markDirtyAndPropogate(); + } +} + +float YGNodeStyleGetFlexGrow(const YGNodeRef node) { + return YGFloatIsUndefined(node->getStyle().flexGrow) + ? kDefaultFlexGrow + : node->getStyle().flexGrow; +} + +float YGNodeStyleGetFlexShrink(const YGNodeRef node) { + return YGFloatIsUndefined(node->getStyle().flexShrink) + ? (node->getConfig()->useWebDefaults ? kWebDefaultFlexShrink + : kDefaultFlexShrink) + : node->getStyle().flexShrink; +} + +#define YG_NODE_STYLE_PROPERTY_SETTER_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + if (node->getStyle().instanceName != paramName) { \ + YGStyle style = node->getStyle(); \ + style.instanceName = paramName; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->getStyle().instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName.unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + style.instanceName = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = \ + YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \ + }; \ + if ((node->getStyle().instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName.unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + \ + style.instanceName = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->getStyle().instanceName.value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName.unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + style.instanceName = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const type paramName) { \ + if (node->getStyle().instanceName.value != paramName || \ + node->getStyle().instanceName.unit != YGUnitPercent) { \ + YGStyle style = node->getStyle(); \ + style.instanceName.value = paramName; \ + style.instanceName.unit = \ + YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \ + if (node->getStyle().instanceName.unit != YGUnitAuto) { \ + YGStyle style = node->getStyle(); \ + style.instanceName.value = YGUndefined; \ + style.instanceName.unit = YGUnitAuto; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } + +#define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->getStyle().instanceName; \ + } + +#define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL( \ + float, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->getStyle().instanceName; \ + } + +#define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL( \ + type, name, paramName, instanceName) \ + YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \ + float, name, paramName, instanceName) \ + \ + type YGNodeStyleGet##name(const YGNodeRef node) { \ + return node->getStyle().instanceName; \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \ + void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \ + if (node->getStyle().instanceName[edge].unit != YGUnitAuto) { \ + YGStyle style = node->getStyle(); \ + style.instanceName[edge].value = YGUndefined; \ + style.instanceName[edge].unit = YGUnitAuto; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->getStyle().instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName[edge].unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + style.instanceName[edge] = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = \ + YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \ + }; \ + if ((node->getStyle().instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName[edge].unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + style.instanceName[edge] = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + WIN_STRUCT(type) \ + YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ + return WIN_STRUCT_REF(node->getStyle().instanceName[edge]); \ + } + +#define YG_NODE_STYLE_EDGE_PROPERTY_IMPL(type, name, paramName, instanceName) \ + void YGNodeStyleSet##name( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + YGValue value = { \ + .value = paramName, \ + .unit = YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \ + }; \ + if ((node->getStyle().instanceName[edge].value != value.value && \ + value.unit != YGUnitUndefined) || \ + node->getStyle().instanceName[edge].unit != value.unit) { \ + YGStyle style = node->getStyle(); \ + style.instanceName[edge] = value; \ + node->setStyle(style); \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + float YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ + return node->getStyle().instanceName[edge].value; \ + } + +#define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \ + type YGNodeLayoutGet##name(const YGNodeRef node) { \ + return node->getLayout().instanceName; \ + } + +#define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \ + type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \ + YGAssertWithNode( \ + node, \ + edge <= YGEdgeEnd, \ + "Cannot get layout properties of multi-edge shorthands"); \ + \ + if (edge == YGEdgeLeft) { \ + if (node->getLayout().direction == YGDirectionRTL) { \ + return node->getLayout().instanceName[YGEdgeEnd]; \ + } else { \ + return node->getLayout().instanceName[YGEdgeStart]; \ + } \ + } \ + \ + if (edge == YGEdgeRight) { \ + if (node->getLayout().direction == YGDirectionRTL) { \ + return node->getLayout().instanceName[YGEdgeStart]; \ + } else { \ + return node->getLayout().instanceName[YGEdgeEnd]; \ + } \ + } \ + \ + return node->getLayout().instanceName[edge]; \ + } + +// YG_NODE_PROPERTY_IMPL(void *, Context, context, context); +// YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print); +// YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout); +// YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType); + +YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction); +YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection); +YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems); +YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf); +YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType); +YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap); +YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow); +YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display); + +YG_NODE_STYLE_PROPERTY_IMPL(float, Flex, flex, flex); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexGrow, flexGrow, flexGrow); +YG_NODE_STYLE_PROPERTY_SETTER_IMPL(float, FlexShrink, flexShrink, flexShrink); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, FlexBasis, flexBasis, flexBasis); + +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin); +YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding); +YG_NODE_STYLE_EDGE_PROPERTY_IMPL(float, Border, border, border); + +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]); + +// Yoga specific properties, not compatible with flexbox specification +YG_NODE_STYLE_PROPERTY_IMPL(float, AspectRatio, aspectRatio, aspectRatio); + +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]); +YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]); +YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction); +YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow); + +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border); +YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding); + +uint32_t gCurrentGenerationCount = 0; + +bool YGLayoutNodeInternal(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const char *reason, + const YGConfigRef config); + +bool YGFloatsEqual(const float a, const float b) { + if (YGFloatIsUndefined(a)) { + return YGFloatIsUndefined(b); + } + return fabs(a - b) < 0.0001f; +} + +static void YGNodePrintInternal(const YGNodeRef node, + const YGPrintOptions options) { + std::string str; + facebook::yoga::YGNodeToString(&str, node, options, 0); + YGLog(node, YGLogLevelDebug, str.c_str()); +} + +void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) { + YGNodePrintInternal(node, options); +} + +const std::array leading = { + {YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}}; + +const std::array trailing = { + {YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}}; +static const std::array pos = {{ + YGEdgeTop, + YGEdgeBottom, + YGEdgeLeft, + YGEdgeRight, +}}; + +static const std::array dim = { + {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}}; + +static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return node->getLeadingPaddingAndBorder(axis, widthSize) + + node->getTrailingPaddingAndBorder(axis, widthSize); +} + +static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) { + const YGAlign align = child->getStyle().alignSelf == YGAlignAuto + ? node->getStyle().alignItems + : child->getStyle().alignSelf; + if (align == YGAlignBaseline && + YGFlexDirectionIsColumn(node->getStyle().flexDirection)) { + return YGAlignFlexStart; + } + return align; +} + +static float YGBaseline(const YGNodeRef node) { + if (node->getBaseline() != nullptr) { + const float baseline = node->getBaseline()( + node, + node->getLayout().measuredDimensions[YGDimensionWidth], + node->getLayout().measuredDimensions[YGDimensionHeight]); + YGAssertWithNode(node, + !YGFloatIsUndefined(baseline), + "Expect custom baseline function to not return NaN"); + return baseline; + } + + YGNodeRef baselineChild = nullptr; + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->getLineIndex() > 0) { + break; + } + if (child->getStyle().positionType == YGPositionTypeAbsolute) { + continue; + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + baselineChild = child; + break; + } + + if (baselineChild == nullptr) { + baselineChild = child; + } + } + + if (baselineChild == nullptr) { + return node->getLayout().measuredDimensions[YGDimensionHeight]; + } + + const float baseline = YGBaseline(baselineChild); + return baseline + baselineChild->getLayout().position[YGEdgeTop]; +} + +static bool YGIsBaselineLayout(const YGNodeRef node) { + if (YGFlexDirectionIsColumn(node->getStyle().flexDirection)) { + return false; + } + if (node->getStyle().alignItems == YGAlignBaseline) { + return true; + } + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->getStyle().positionType == YGPositionTypeRelative && + child->getStyle().alignSelf == YGAlignBaseline) { + return true; + } + } + + return false; +} + +static inline float YGNodeDimWithMargin(const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return node->getLayout().measuredDimensions[dim[axis]] + + node->getLeadingMargin(axis, widthSize) + + node->getTrailingMargin(axis, widthSize); +} + +static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node, + const YGFlexDirection axis, + const float parentSize) { + return !( + node->getResolvedDimension(dim[axis]).unit == YGUnitAuto || + node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint && + node->getResolvedDimension(dim[axis]).value < 0.0f) || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent && + (node->getResolvedDimension(dim[axis]).value < 0.0f || + YGFloatIsUndefined(parentSize)))); +} + +static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) { + const float value = node->getLayout().measuredDimensions[dim[axis]]; + return !YGFloatIsUndefined(value) && value >= 0.0f; +} + +static float YGNodeBoundAxisWithinMinAndMax(const YGNodeRef node, + const YGFlexDirection axis, + const float value, + const float axisSize) { + float min = YGUndefined; + float max = YGUndefined; + + if (YGFlexDirectionIsColumn(axis)) { + min = YGResolveValue( + node->getStyle().minDimensions[YGDimensionHeight], axisSize); + max = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], axisSize); + } else if (YGFlexDirectionIsRow(axis)) { + min = YGResolveValue( + node->getStyle().minDimensions[YGDimensionWidth], axisSize); + max = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], axisSize); + } + + float boundValue = value; + + if (!YGFloatIsUndefined(max) && max >= 0.0f && boundValue > max) { + boundValue = max; + } + + if (!YGFloatIsUndefined(min) && min >= 0.0f && boundValue < min) { + boundValue = min; + } + + return boundValue; +} + +// Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go +// below the +// padding and border amount. +static inline float YGNodeBoundAxis(const YGNodeRef node, + const YGFlexDirection axis, + const float value, + const float axisSize, + const float widthSize) { + return fmaxf(YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize), + YGNodePaddingAndBorderForAxis(node, axis, widthSize)); +} + +static void YGNodeSetChildTrailingPosition(const YGNodeRef node, + const YGNodeRef child, + const YGFlexDirection axis) { + const float size = child->getLayout().measuredDimensions[dim[axis]]; + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[axis]] - size - + child->getLayout().position[pos[axis]], + trailing[axis]); +} + +static void YGConstrainMaxSizeForMode(const YGNodeRef node, + const enum YGFlexDirection axis, + const float parentAxisSize, + const float parentWidth, + YGMeasureMode *mode, + float *size) { + const float maxSize = + YGResolveValue( + node->getStyle().maxDimensions[dim[axis]], parentAxisSize) + + node->getMarginForAxis(axis, parentWidth); + switch (*mode) { + case YGMeasureModeExactly: + case YGMeasureModeAtMost: + *size = (YGFloatIsUndefined(maxSize) || *size < maxSize) ? *size : maxSize; + break; + case YGMeasureModeUndefined: + if (!YGFloatIsUndefined(maxSize)) { + *mode = YGMeasureModeAtMost; + *size = maxSize; + } + break; + } +} + +static void YGNodeComputeFlexBasisForChild(const YGNodeRef node, + const YGNodeRef child, + const float width, + const YGMeasureMode widthMode, + const float height, + const float parentWidth, + const float parentHeight, + const YGMeasureMode heightMode, + const YGDirection direction, + const YGConfigRef config) { + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->getStyle().flexDirection, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const float mainAxisSize = isMainAxisRow ? width : height; + const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; + + float childWidth; + float childHeight; + YGMeasureMode childWidthMeasureMode; + YGMeasureMode childHeightMeasureMode; + + const float resolvedFlexBasis = + YGResolveValue(child->resolveFlexBasisPtr(), mainAxisParentSize); + const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, parentWidth); + const bool isColumnStyleDimDefined = + YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, parentHeight); + + if (!YGFloatIsUndefined(resolvedFlexBasis) && !YGFloatIsUndefined(mainAxisSize)) { + if (YGFloatIsUndefined(child->getLayout().computedFlexBasis) || + (YGConfigIsExperimentalFeatureEnabled( + child->getConfig(), YGExperimentalFeatureWebFlexBasis) && + child->getLayout().computedFlexBasisGeneration != + gCurrentGenerationCount)) { + child->setLayoutComputedFlexBasis(fmaxf( + resolvedFlexBasis, + YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth))); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + child->setLayoutComputedFlexBasis(fmaxf( + YGResolveValue( + child->getResolvedDimension(YGDimensionWidth), parentWidth), + YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, parentWidth))); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + child->setLayoutComputedFlexBasis(fmaxf( + YGResolveValue( + child->getResolvedDimension(YGDimensionHeight), parentHeight), + YGNodePaddingAndBorderForAxis( + child, YGFlexDirectionColumn, parentWidth))); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped + // flex basis). + childWidth = YGUndefined; + childHeight = YGUndefined; + childWidthMeasureMode = YGMeasureModeUndefined; + childHeightMeasureMode = YGMeasureModeUndefined; + + const float marginRow = + child->getMarginForAxis(YGFlexDirectionRow, parentWidth); + const float marginColumn = + child->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + + if (isRowStyleDimDefined) { + childWidth = + YGResolveValue( + child->getResolvedDimension(YGDimensionWidth), parentWidth) + + marginRow; + childWidthMeasureMode = YGMeasureModeExactly; + } + if (isColumnStyleDimDefined) { + childHeight = + YGResolveValue( + child->getResolvedDimension(YGDimensionHeight), parentHeight) + + marginColumn; + childHeightMeasureMode = YGMeasureModeExactly; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if ((!isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) || + node->getStyle().overflow != YGOverflowScroll) { + if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + } + + if ((isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) || + node->getStyle().overflow != YGOverflowScroll) { + if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) { + childHeight = height; + childHeightMeasureMode = YGMeasureModeAtMost; + } + } + + if (!YGFloatIsUndefined(child->getStyle().aspectRatio)) { + if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) { + childHeight = marginColumn + + (childWidth - marginRow) / child->getStyle().aspectRatio; + childHeightMeasureMode = YGMeasureModeExactly; + } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) { + childWidth = marginRow + + (childHeight - marginColumn) * child->getStyle().aspectRatio; + childWidthMeasureMode = YGMeasureModeExactly; + } + } + + // If child has no defined size in the cross axis and is set to stretch, + // set the cross + // axis to be measured exactly with the available inner width + + const bool hasExactWidth = !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly; + const bool childWidthStretch = YGNodeAlignItem(node, child) == YGAlignStretch && + childWidthMeasureMode != YGMeasureModeExactly; + if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && childWidthStretch) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeExactly; + if (!YGFloatIsUndefined(child->getStyle().aspectRatio)) { + childHeight = (childWidth - marginRow) / child->getStyle().aspectRatio; + childHeightMeasureMode = YGMeasureModeExactly; + } + } + + const bool hasExactHeight = !YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly; + const bool childHeightStretch = YGNodeAlignItem(node, child) == YGAlignStretch && + childHeightMeasureMode != YGMeasureModeExactly; + if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && childHeightStretch) { + childHeight = height; + childHeightMeasureMode = YGMeasureModeExactly; + + if (!YGFloatIsUndefined(child->getStyle().aspectRatio)) { + childWidth = + (childHeight - marginColumn) * child->getStyle().aspectRatio; + childWidthMeasureMode = YGMeasureModeExactly; + } + } + + YGConstrainMaxSizeForMode( + child, YGFlexDirectionRow, parentWidth, parentWidth, &childWidthMeasureMode, &childWidth); + YGConstrainMaxSizeForMode(child, + YGFlexDirectionColumn, + parentHeight, + parentWidth, + &childHeightMeasureMode, + &childHeight); + + // Measure the child + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + parentWidth, + parentHeight, + false, + "measure", + config); + + child->setLayoutComputedFlexBasis(fmaxf( + child->getLayout().measuredDimensions[dim[mainAxis]], + YGNodePaddingAndBorderForAxis(child, mainAxis, parentWidth))); + } + child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount); +} + +static void YGNodeAbsoluteLayoutChild(const YGNodeRef node, + const YGNodeRef child, + const float width, + const YGMeasureMode widthMode, + const float height, + const YGDirection direction, + const YGConfigRef config) { + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->getStyle().flexDirection, direction); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + + float childWidth = YGUndefined; + float childHeight = YGUndefined; + YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined; + YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined; + + const float marginRow = child->getMarginForAxis(YGFlexDirectionRow, width); + const float marginColumn = + child->getMarginForAxis(YGFlexDirectionColumn, width); + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) { + childWidth = + YGResolveValue(child->getResolvedDimension(YGDimensionWidth), width) + + marginRow; + } else { + // If the child doesn't have a specified width, compute the width based + // on the left/right + // offsets if they're defined. + if (child->isLeadingPositionDefined(YGFlexDirectionRow) && + child->isTrailingPosDefined(YGFlexDirectionRow)) { + childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] - + (node->getLeadingBorder(YGFlexDirectionRow) + + node->getTrailingBorder(YGFlexDirectionRow)) - + (child->getLeadingPosition(YGFlexDirectionRow, width) + + child->getTrailingPosition(YGFlexDirectionRow, width)); + childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width); + } + } + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { + childHeight = + YGResolveValue(child->getResolvedDimension(YGDimensionHeight), height) + + marginColumn; + } else { + // If the child doesn't have a specified height, compute the height + // based on the top/bottom + // offsets if they're defined. + if (child->isLeadingPositionDefined(YGFlexDirectionColumn) && + child->isTrailingPosDefined(YGFlexDirectionColumn)) { + childHeight = node->getLayout().measuredDimensions[YGDimensionHeight] - + (node->getLeadingBorder(YGFlexDirectionColumn) + + node->getTrailingBorder(YGFlexDirectionColumn)) - + (child->getLeadingPosition(YGFlexDirectionColumn, height) + + child->getTrailingPosition(YGFlexDirectionColumn, height)); + childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width); + } + } + + // 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 (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) { + if (!YGFloatIsUndefined(child->getStyle().aspectRatio)) { + if (YGFloatIsUndefined(childWidth)) { + childWidth = marginRow + + (childHeight - marginColumn) * child->getStyle().aspectRatio; + } else if (YGFloatIsUndefined(childHeight)) { + childHeight = marginColumn + + (childWidth - marginRow) / child->getStyle().aspectRatio; + } + } + } + + // If we're still missing one or the other dimension, measure the content. + if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) { + childWidthMeasureMode = + YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly; + childHeightMeasureMode = + YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly; + + // If the size of the parent is defined then try to constrain the absolute child to that size + // as well. This allows text within the absolute child to wrap to the size of its parent. + // This is the same behavior as many browsers implement. + if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && widthMode != YGMeasureModeUndefined && + width > 0) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + childWidth, + childHeight, + false, + "abs-measure", + config); + childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] + + child->getMarginForAxis(YGFlexDirectionRow, width); + childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis(YGFlexDirectionColumn, width); + } + + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + childWidth, + childHeight, + true, + "abs-layout", + config); + + if (child->isTrailingPosDefined(mainAxis) && + !child->isLeadingPositionDefined(mainAxis)) { + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[mainAxis]] - + child->getLayout().measuredDimensions[dim[mainAxis]] - + node->getTrailingBorder(mainAxis) - + child->getTrailingMargin(mainAxis, width) - + child->getTrailingPosition( + mainAxis, isMainAxisRow ? width : height), + leading[mainAxis]); + } else if ( + !child->isLeadingPositionDefined(mainAxis) && + node->getStyle().justifyContent == YGJustifyCenter) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[mainAxis]] - + child->getLayout().measuredDimensions[dim[mainAxis]]) / + 2.0f, + leading[mainAxis]); + } else if ( + !child->isLeadingPositionDefined(mainAxis) && + node->getStyle().justifyContent == YGJustifyFlexEnd) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[mainAxis]] - + child->getLayout().measuredDimensions[dim[mainAxis]]), + leading[mainAxis]); + } + + if (child->isTrailingPosDefined(crossAxis) && + !child->isLeadingPositionDefined(crossAxis)) { + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]] - + node->getTrailingBorder(crossAxis) - + child->getTrailingMargin(crossAxis, width) - + child->getTrailingPosition( + crossAxis, isMainAxisRow ? height : width), + leading[crossAxis]); + + } else if ( + !child->isLeadingPositionDefined(crossAxis) && + YGNodeAlignItem(node, child) == YGAlignCenter) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]]) / + 2.0f, + leading[crossAxis]); + } else if ( + !child->isLeadingPositionDefined(crossAxis) && + ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^ + (node->getStyle().flexWrap == YGWrapWrapReverse))) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]]), + leading[crossAxis]); + } +} + +static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + YGAssertWithNode( + node, + node->getMeasure() != nullptr, + "Expected node to have custom measure function"); + + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, availableWidth); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, availableWidth); + + // We want to make sure we don't call measure with negative size + const float innerWidth = YGFloatIsUndefined(availableWidth) + ? availableWidth + : fmaxf(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + const float innerHeight = YGFloatIsUndefined(availableHeight) + ? availableHeight + : fmaxf( + 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); + + if (widthMeasureMode == YGMeasureModeExactly && + heightMeasureMode == YGMeasureModeExactly) { + // Don't bother sizing the text if both dimensions are already defined. + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + availableWidth - marginAxisRow, + parentWidth, + parentWidth), + YGDimensionWidth); + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + parentHeight, + parentWidth), + YGDimensionHeight); + } else { + // Measure the text under the current constraints. + const YGSize measuredSize = node->getMeasure()( + node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + parentWidth, + parentWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth), + YGDimensionHeight); + } +} + +// For nodes with no children, use the available values if they were provided, +// or the minimum size as indicated by the padding and border sizes. +static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, parentWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, parentWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + parentWidth, + parentWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth), + YGDimensionHeight); +} + +static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight) { + if ((widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || + (heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || + (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly)) { + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + YGFloatIsUndefined(availableWidth) || + (widthMeasureMode == YGMeasureModeAtMost && + availableWidth < 0.0f) + ? 0.0f + : availableWidth - marginAxisRow, + parentWidth, + parentWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + YGFloatIsUndefined(availableHeight) || + (heightMeasureMode == YGMeasureModeAtMost && + availableHeight < 0.0f) + ? 0.0f + : availableHeight - marginAxisColumn, + parentHeight, + parentWidth), + YGDimensionHeight); + return true; + } + + return false; +} + +static void YGZeroOutLayoutRecursivly(const YGNodeRef node) { + memset(&(node->getLayout()), 0, sizeof(YGLayout)); + node->setHasNewLayout(true); + node->cloneChildrenIfNeeded(); + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->getChild(i); + YGZeroOutLayoutRecursivly(child); + } +} + +static float YGNodeCalculateAvailableInnerDim( + const YGNodeRef node, + YGFlexDirection axis, + float availableDim, + float parentDim) { + YGFlexDirection direction = + YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn; + YGDimension dimension = + YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight; + + const float margin = node->getMarginForAxis(direction, parentDim); + const float paddingAndBorder = + YGNodePaddingAndBorderForAxis(node, direction, parentDim); + + float availableInnerDim = availableDim - margin - paddingAndBorder; + // Max dimension overrides predefined dimension value; Min dimension in turn + // overrides both of the above + if (!YGFloatIsUndefined(availableInnerDim)) { + // We want to make sure our available height does not violate min and max + // constraints + const float minInnerDim = + YGResolveValue(node->getStyle().minDimensions[dimension], parentDim) - + paddingAndBorder; + const float maxInnerDim = + YGResolveValue(node->getStyle().maxDimensions[dimension], parentDim) - + paddingAndBorder; + availableInnerDim = + fmaxf(fminf(availableInnerDim, maxInnerDim), minInnerDim); + } + + return availableInnerDim; +} + +static void YGNodeComputeFlexBasisForChildren( + const YGNodeRef node, + const float availableInnerWidth, + const float availableInnerHeight, + YGMeasureMode widthMeasureMode, + YGMeasureMode heightMeasureMode, + YGDirection direction, + YGFlexDirection mainAxis, + const YGConfigRef config, + bool performLayout, + float& totalOuterFlexBasis) { + YGNodeRef singleFlexChild = nullptr; + YGVector children = node->getChildren(); + YGMeasureMode measureModeMainDim = + YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode; + // If there is only one child with flexGrow + flexShrink it means we can set + // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the + // child to exactly match the remaining space + if (measureModeMainDim == YGMeasureModeExactly) { + for (auto child : children) { + if (singleFlexChild != nullptr) { + if (child->isNodeFlexible()) { + // There is already a flexible child, abort + singleFlexChild = nullptr; + break; + } + } else if ( + child->resolveFlexGrow() > 0.0f && + child->resolveFlexShrink() > 0.0f) { + singleFlexChild = child; + } + } + } + + for (auto child : children) { + child->resolveDimension(); + if (child->getStyle().display == YGDisplayNone) { + YGZeroOutLayoutRecursivly(child); + child->setHasNewLayout(true); + child->setDirty(false); + continue; + } + if (performLayout) { + // Set the initial position (relative to the parent). + const YGDirection childDirection = child->resolveDirection(direction); + const float mainDim = YGFlexDirectionIsRow(mainAxis) + ? availableInnerWidth + : availableInnerHeight; + const float crossDim = YGFlexDirectionIsRow(mainAxis) + ? availableInnerHeight + : availableInnerWidth; + child->setPosition( + childDirection, mainDim, crossDim, availableInnerWidth); + } + + if (child->getStyle().positionType == YGPositionTypeAbsolute) { + continue; + } + if (child == singleFlexChild) { + child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount); + child->setLayoutComputedFlexBasis(0); + } else { + YGNodeComputeFlexBasisForChild( + node, + child, + availableInnerWidth, + widthMeasureMode, + availableInnerHeight, + availableInnerWidth, + availableInnerHeight, + heightMeasureMode, + direction, + config); + } + + totalOuterFlexBasis += child->getLayout().computedFlexBasis + + child->getMarginForAxis(mainAxis, availableInnerWidth); + } +} + +// This function assumes that all the children of node have their +// computedFlexBasis properly computed(To do this use +// YGNodeComputeFlexBasisForChildren function). +// This function calculates YGCollectFlexItemsRowMeasurement +static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( + const YGNodeRef& node, + const YGDirection parentDirection, + const float mainAxisParentSize, + const float availableInnerWidth, + const float availableInnerMainDim, + const uint32_t startOfLineIndex, + const uint32_t lineCount) { + YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {}; + flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size()); + + float sizeConsumedOnCurrentLineIncludingMinConstraint = 0; + const YGFlexDirection mainAxis = YGResolveFlexDirection( + node->getStyle().flexDirection, node->resolveDirection(parentDirection)); + const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap; + + // Add items to the current line until it's full or we run out of items. + uint32_t endOfLineIndex = startOfLineIndex; + for (; endOfLineIndex < node->getChildrenCount(); endOfLineIndex++) { + const YGNodeRef child = node->getChild(endOfLineIndex); + if (child->getStyle().display == YGDisplayNone || + child->getStyle().positionType == YGPositionTypeAbsolute) { + continue; + } + child->setLineIndex(lineCount); + const float childMarginMainAxis = + child->getMarginForAxis(mainAxis, availableInnerWidth); + const float flexBasisWithMinAndMaxConstraints = + YGNodeBoundAxisWithinMinAndMax( + child, + mainAxis, + child->getLayout().computedFlexBasis, + mainAxisParentSize); + + // If this is a multi-line flow and this item pushes us over the + // available size, we've + // hit the end of the current line. Break out of the loop and lay out + // the current line. + if (sizeConsumedOnCurrentLineIncludingMinConstraint + + flexBasisWithMinAndMaxConstraints + childMarginMainAxis > + availableInnerMainDim && + isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) { + break; + } + + sizeConsumedOnCurrentLineIncludingMinConstraint += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + flexAlgoRowMeasurement.sizeConsumedOnCurrentLine += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + flexAlgoRowMeasurement.itemsOnLine++; + + if (child->isNodeFlexible()) { + flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow(); + + // Unlike the grow factor, the shrink factor is scaled relative to the + // child dimension. + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors += + -child->resolveFlexShrink() * child->getLayout().computedFlexBasis; + } + + flexAlgoRowMeasurement.relativeChildren.push_back(child); + } + + // The total flex factor needs to be floored to 1. + if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 && + flexAlgoRowMeasurement.totalFlexGrowFactors < 1) { + flexAlgoRowMeasurement.totalFlexGrowFactors = 1; + } + + // The total flex shrink factor needs to be floored to 1. + if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 && + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) { + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1; + } + flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex; + return flexAlgoRowMeasurement; +} + +// It distributes the free space to the flexible items and ensures that the size +// of the flex items abide the min and max constraints. At the end of this +// function the child nodes would have proper size. Prior using this function +// please ensure that YGDistributeFreeSpaceFirstPass is called. +static float YGDistributeFreeSpaceSecondPass( + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGNodeRef node, + const YGFlexDirection mainAxis, + const YGFlexDirection crossAxis, + const float mainAxisParentSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config) { + float childFlexBasis = 0; + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float deltaFreeSpace = 0; + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap; + + for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { + childFlexBasis = YGNodeBoundAxisWithinMinAndMax( + currentRelativeChild, + mainAxis, + currentRelativeChild->getLayout().computedFlexBasis, + mainAxisParentSize); + float updatedMainSize = childFlexBasis; + + if (collectedFlexItemsValues.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentRelativeChild->resolveFlexShrink() * childFlexBasis; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize; + + if (collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = childFlexBasis + + (collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors) * + flexShrinkScaledFactor; + } + + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if (collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * + flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace += updatedMainSize - childFlexBasis; + + const float marginMain = + currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth); + const float marginCross = + currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth); + + float childCrossSize; + float childMainSize = updatedMainSize + marginMain; + YGMeasureMode childCrossMeasureMode; + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + + if (!YGFloatIsUndefined(currentRelativeChild->getStyle().aspectRatio)) { + childCrossSize = isMainAxisRow ? (childMainSize - marginMain) / + currentRelativeChild->getStyle().aspectRatio + : (childMainSize - marginMain) * + currentRelativeChild->getStyle().aspectRatio; + childCrossMeasureMode = YGMeasureModeExactly; + + childCrossSize += marginCross; + } else if ( + !YGFloatIsUndefined(availableInnerCrossDim) && + !YGNodeIsStyleDimDefined( + currentRelativeChild, crossAxis, availableInnerCrossDim) && + measureModeCrossDim == YGMeasureModeExactly && + !(isNodeFlexWrap && flexBasisOverflows) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + currentRelativeChild->marginLeadingValue(crossAxis).unit != + YGUnitAuto && + currentRelativeChild->marginTrailingValue(crossAxis).unit != + YGUnitAuto) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = YGMeasureModeExactly; + } else if (!YGNodeIsStyleDimDefined( + currentRelativeChild, crossAxis, availableInnerCrossDim)) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = YGFloatIsUndefined(childCrossSize) + ? YGMeasureModeUndefined + : YGMeasureModeAtMost; + } else { + childCrossSize = + YGResolveValue( + currentRelativeChild->getResolvedDimension(dim[crossAxis]), + availableInnerCrossDim) + + marginCross; + const bool isLoosePercentageMeasurement = + currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit == + YGUnitPercent && + measureModeCrossDim != YGMeasureModeExactly; + childCrossMeasureMode = + YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + } + + YGConstrainMaxSizeForMode( + currentRelativeChild, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize); + YGConstrainMaxSizeForMode( + currentRelativeChild, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize); + + const bool requiresStretchLayout = + !YGNodeIsStyleDimDefined( + currentRelativeChild, crossAxis, availableInnerCrossDim) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + currentRelativeChild->marginLeadingValue(crossAxis).unit != + YGUnitAuto && + currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto; + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const YGMeasureMode childWidthMeasureMode = + isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + const YGMeasureMode childHeightMeasureMode = + !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + + // Recursively call the layout algorithm for this child with the updated + // main size. + YGLayoutNodeInternal( + currentRelativeChild, + childWidth, + childHeight, + node->getLayout().direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + performLayout && !requiresStretchLayout, + "flex", + config); + node->setLayoutHadOverflow( + node->getLayout().hadOverflow | + currentRelativeChild->getLayout().hadOverflow); + } + return deltaFreeSpace; +} + +// It distributes the free space to the flexible items.For those flexible items +// whose min and max constraints are triggered, those flex item's clamped size +// is removed from the remaingfreespace. +static void YGDistributeFreeSpaceFirstPass( + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGFlexDirection mainAxis, + const float mainAxisParentSize, + const float availableInnerMainDim, + const float availableInnerWidth) { + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float baseMainSize = 0; + float boundMainSize = 0; + float deltaFreeSpace = 0; + + for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { + float childFlexBasis = YGNodeBoundAxisWithinMinAndMax( + currentRelativeChild, + mainAxis, + currentRelativeChild->getLayout().computedFlexBasis, + mainAxisParentSize); + + if (collectedFlexItemsValues.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentRelativeChild->resolveFlexShrink() * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors * + flexShrinkScaledFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, + // this item's + // min/max constraints should also trigger in the second pass + // resulting in the + // item's size calculation being identical in the first and second + // passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + collectedFlexItemsValues.totalFlexShrinkScaledFactors -= + flexShrinkScaledFactor; + } + } + } else if (collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, + // this item's + // min/max constraints should also trigger in the second pass + // resulting in the + // item's size calculation being identical in the first and second + // passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor; + } + } + } + } + collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace; +} + +// Do two passes over the flex items to figure out how to distribute the +// remaining space. +// The first pass finds the items whose min/max constraints trigger, +// freezes them at those +// sizes, and excludes those sizes from the remaining space. The second +// pass sets the size +// of each flexible item. It distributes the remaining space amongst the +// items whose min/max +// constraints didn't trigger in pass 1. For the other items, it sets +// their sizes by forcing +// their min/max constraints to trigger again. +// +// This two pass approach for resolving min/max constraints deviates from +// the spec. The +// spec (https://www.w3.org/TR/YG-flexbox-1/#resolve-flexible-lengths) +// describes a process +// that needs to be repeated a variable number of times. The algorithm +// implemented here +// won't handle all cases but it was simpler to implement and it mitigates +// performance +// concerns because we know exactly how many passes it'll do. +// +// At the end of this function the child nodes would have the proper size +// assigned to them. +// +static void YGResolveFlexibleLength( + const YGNodeRef node, + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGFlexDirection mainAxis, + const YGFlexDirection crossAxis, + const float mainAxisParentSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config) { + const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace; + // First pass: detect the flex items whose min/max constraints trigger + YGDistributeFreeSpaceFirstPass( + collectedFlexItemsValues, + mainAxis, + mainAxisParentSize, + availableInnerMainDim, + availableInnerWidth); + + // Second pass: resolve the sizes of the flexible items + const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass( + collectedFlexItemsValues, + node, + mainAxis, + crossAxis, + mainAxisParentSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config); + + collectedFlexItemsValues.remainingFreeSpace = + originalFreeSpace - distributedFreeSpace; +} + +static void YGJustifyMainAxis( + const YGNodeRef node, + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const uint32_t& startOfLineIndex, + const YGFlexDirection& mainAxis, + const YGFlexDirection& crossAxis, + const YGMeasureMode& measureModeMainDim, + const YGMeasureMode& measureModeCrossDim, + const float& mainAxisParentSize, + const float& parentWidth, + const float& availableInnerMainDim, + const float& availableInnerCrossDim, + const float& availableInnerWidth, + const bool& performLayout) { + const YGStyle style = node->getStyle(); + + // If we are using "at most" rules in the main axis. Calculate the remaining + // space when constraint by the min size defined for the main axis. + if (measureModeMainDim == YGMeasureModeAtMost && + collectedFlexItemsValues.remainingFreeSpace > 0) { + if (style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined && + YGResolveValue( + style.minDimensions[dim[mainAxis]], mainAxisParentSize) >= 0) { + collectedFlexItemsValues.remainingFreeSpace = fmaxf( + 0, + YGResolveValue( + style.minDimensions[dim[mainAxis]], mainAxisParentSize) - + (availableInnerMainDim - + collectedFlexItemsValues.remainingFreeSpace)); + } else { + collectedFlexItemsValues.remainingFreeSpace = 0; + } + } + + int numberOfAutoMarginsOnCurrentLine = 0; + for (uint32_t i = startOfLineIndex; + i < collectedFlexItemsValues.endOfLineIndex; + i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().positionType == YGPositionTypeRelative) { + if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + } + } + + // In order to position the elements in the main axis, we have two + // controls. The space between the beginning and the first element + // and the space between each two elements. + float leadingMainDim = 0; + float betweenMainDim = 0; + const YGJustify justifyContent = node->getStyle().justifyContent; + + if (numberOfAutoMarginsOnCurrentLine == 0) { + switch (justifyContent) { + case YGJustifyCenter: + leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2; + break; + case YGJustifyFlexEnd: + leadingMainDim = collectedFlexItemsValues.remainingFreeSpace; + break; + case YGJustifySpaceBetween: + if (collectedFlexItemsValues.itemsOnLine > 1) { + betweenMainDim = + fmaxf(collectedFlexItemsValues.remainingFreeSpace, 0) / + (collectedFlexItemsValues.itemsOnLine - 1); + } else { + betweenMainDim = 0; + } + break; + case YGJustifySpaceEvenly: + // Space is distributed evenly across all elements + betweenMainDim = collectedFlexItemsValues.remainingFreeSpace / + (collectedFlexItemsValues.itemsOnLine + 1); + leadingMainDim = betweenMainDim; + break; + case YGJustifySpaceAround: + // Space on the edges is half of the space between elements + betweenMainDim = collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.itemsOnLine; + leadingMainDim = betweenMainDim / 2; + break; + case YGJustifyFlexStart: + break; + } + } + + const float leadingPaddingAndBorderMain = + node->getLeadingPaddingAndBorder(mainAxis, parentWidth); + collectedFlexItemsValues.mainDim = + leadingPaddingAndBorderMain + leadingMainDim; + collectedFlexItemsValues.crossDim = 0; + + for (uint32_t i = startOfLineIndex; + i < collectedFlexItemsValues.endOfLineIndex; + i++) { + const YGNodeRef child = node->getChild(i); + const YGStyle childStyle = child->getStyle(); + const YGLayout childLayout = child->getLayout(); + if (childStyle.display == YGDisplayNone) { + continue; + } + if (childStyle.positionType == YGPositionTypeAbsolute && + child->isLeadingPositionDefined(mainAxis)) { + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child->setLayoutPosition( + child->getLeadingPosition(mainAxis, availableInnerMainDim) + + node->getLeadingBorder(mainAxis) + + child->getLeadingMargin(mainAxis, availableInnerWidth), + pos[mainAxis]); + } + } else { + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements + // do not take part in that phase. + if (childStyle.positionType == YGPositionTypeRelative) { + if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { + collectedFlexItemsValues.mainDim += + collectedFlexItemsValues.remainingFreeSpace / + numberOfAutoMarginsOnCurrentLine; + } + + if (performLayout) { + child->setLayoutPosition( + childLayout.position[pos[mainAxis]] + + collectedFlexItemsValues.mainDim, + pos[mainAxis]); + } + + if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) { + collectedFlexItemsValues.mainDim += + collectedFlexItemsValues.remainingFreeSpace / + numberOfAutoMarginsOnCurrentLine; + } + bool canSkipFlex = + !performLayout && measureModeCrossDim == YGMeasureModeExactly; + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the + // measuredDims because + // they weren't computed. This means we can't call + // YGNodeDimWithMargin. + collectedFlexItemsValues.mainDim += betweenMainDim + + child->getMarginForAxis(mainAxis, availableInnerWidth) + + childLayout.computedFlexBasis; + collectedFlexItemsValues.crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + collectedFlexItemsValues.mainDim += betweenMainDim + + YGNodeDimWithMargin(child, mainAxis, availableInnerWidth); + + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension. + collectedFlexItemsValues.crossDim = fmaxf( + collectedFlexItemsValues.crossDim, + YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); + } + } else if (performLayout) { + child->setLayoutPosition( + childLayout.position[pos[mainAxis]] + + node->getLeadingBorder(mainAxis) + leadingMainDim, + pos[mainAxis]); + } + } + } + collectedFlexItemsValues.mainDim += + node->getTrailingPaddingAndBorder(mainAxis, parentWidth); +} + +// +// This is the main routine that implements a subset of the flexbox layout +// algorithm +// described in the W3C YG documentation: https://www.w3.org/TR/YG3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, +// which +// are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes +// are +// stacked in document order. +// * The 'order' property is not supported. The order of flex items is always +// defined +// by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of +// 'collapse' +// and 'hidden' are not supported. +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or +// bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default +// minimum +// main size. For text blocks, for example, this is the width of the widest +// word. +// Calculating the minimum width is expensive, so we forego it and assume a +// default +// minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible +// lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', +// but +// the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and layed out +// - availableWidth & availableHeight: available size to be used for sizing +// the node +// or YGUndefined if the size is not available; interpretation depends on +// layout +// flags +// - parentDirection: the inline (text) direction within the parent +// (left-to-right or +// right-to-left) +// - widthMeasureMode: indicates the sizing rules for the width (see below +// for explanation) +// - heightMeasureMode: indicates the sizing rules for the height (see below +// for explanation) +// - performLayout: specifies whether the caller is interested in just the +// dimensions +// of the node or it requires the entire node and its subtree to be layed +// out +// (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox +// elements. It uses the +// information in node.style, which is treated as a read-only input. It is +// responsible for +// setting the layout.direction and layout.measuredDimensions fields for the +// input node as well +// as the layout.position and layout.lineIndex fields for its child nodes. +// The +// layout.measuredDimensions field includes any border or padding for the +// node but does +// not include margins. +// +// The spec describes four different layout modes: "fill available", "max +// content", "min +// content", +// and "fit content". Of these, we don't use "min content" because we don't +// support default +// minimum main sizes (see above for details). Each of our measure modes maps +// to a layout mode +// from the spec (https://www.w3.org/TR/YG3-sizing/#terms): +// - YGMeasureModeUndefined: max content +// - YGMeasureModeExactly: fill available +// - YGMeasureModeAtMost: fit content +// +// When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes +// an available size of +// undefined then it must also pass a measure mode of YGMeasureModeUndefined +// in that dimension. +// +static void YGNodelayoutImpl(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const YGConfigRef config) { + YGAssertWithNode(node, + YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined + : true, + "availableWidth is indefinite so widthMeasureMode must be " + "YGMeasureModeUndefined"); + YGAssertWithNode(node, + YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined + : true, + "availableHeight is indefinite so heightMeasureMode must be " + "YGMeasureModeUndefined"); + + // Set the resolved resolution in the node's layout. + const YGDirection direction = node->resolveDirection(parentDirection); + node->setLayoutDirection(direction); + + const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction); + const YGFlexDirection flexColumnDirection = + YGResolveFlexDirection(YGFlexDirectionColumn, direction); + + node->setLayoutMargin( + node->getLeadingMargin(flexRowDirection, parentWidth), YGEdgeStart); + node->setLayoutMargin( + node->getTrailingMargin(flexRowDirection, parentWidth), YGEdgeEnd); + node->setLayoutMargin( + node->getLeadingMargin(flexColumnDirection, parentWidth), YGEdgeTop); + node->setLayoutMargin( + node->getTrailingMargin(flexColumnDirection, parentWidth), YGEdgeBottom); + + node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), YGEdgeStart); + node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), YGEdgeEnd); + node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop); + node->setLayoutBorder( + node->getTrailingBorder(flexColumnDirection), YGEdgeBottom); + + node->setLayoutPadding( + node->getLeadingPadding(flexRowDirection, parentWidth), YGEdgeStart); + node->setLayoutPadding( + node->getTrailingPadding(flexRowDirection, parentWidth), YGEdgeEnd); + node->setLayoutPadding( + node->getLeadingPadding(flexColumnDirection, parentWidth), YGEdgeTop); + node->setLayoutPadding( + node->getTrailingPadding(flexColumnDirection, parentWidth), YGEdgeBottom); + + if (node->getMeasure() != nullptr) { + YGNodeWithMeasureFuncSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight); + return; + } + + const uint32_t childCount = YGNodeGetChildCount(node); + if (childCount == 0) { + YGNodeEmptyContainerSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight); + return; + } + + // If we're not being asked to perform a full layout we can skip the algorithm if we already know + // the size + if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight)) { + return; + } + + // At this point we know we're going to perform work. Ensure that each child has a mutable copy. + node->cloneChildrenIfNeeded(); + // Reset layout flags, as they could have changed. + node->setLayoutHadOverflow(false); + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->getStyle().flexDirection, direction); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap; + + const float mainAxisParentSize = isMainAxisRow ? parentWidth : parentHeight; + const float crossAxisParentSize = isMainAxisRow ? parentHeight : parentWidth; + + const float leadingPaddingAndBorderCross = + node->getLeadingPaddingAndBorder(crossAxis, parentWidth); + const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, parentWidth); + const float paddingAndBorderAxisCross = + YGNodePaddingAndBorderForAxis(node, crossAxis, parentWidth); + + YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + const float paddingAndBorderAxisRow = + isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; + const float paddingAndBorderAxisColumn = + isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; + + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + + const float minInnerWidth = + YGResolveValue( + node->getStyle().minDimensions[YGDimensionWidth], parentWidth) - + paddingAndBorderAxisRow; + const float maxInnerWidth = + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], parentWidth) - + paddingAndBorderAxisRow; + const float minInnerHeight = + YGResolveValue( + node->getStyle().minDimensions[YGDimensionHeight], parentHeight) - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], parentHeight) - + paddingAndBorderAxisColumn; + + const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight; + const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + + float availableInnerWidth = YGNodeCalculateAvailableInnerDim( + node, YGFlexDirectionRow, availableWidth, parentWidth); + float availableInnerHeight = YGNodeCalculateAvailableInnerDim( + node, YGFlexDirectionColumn, availableHeight, parentHeight); + + float availableInnerMainDim = + isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = + isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + float totalOuterFlexBasis = 0; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + + YGNodeComputeFlexBasisForChildren( + node, + availableInnerWidth, + availableInnerHeight, + widthMeasureMode, + heightMeasureMode, + direction, + mainAxis, + config, + performLayout, + totalOuterFlexBasis); + + const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined + ? false + : totalOuterFlexBasis > availableInnerMainDim; + if (isNodeFlexWrap && flexBasisOverflows && + measureModeMainDim == YGMeasureModeAtMost) { + measureModeMainDim = YGMeasureModeExactly; + } + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + uint32_t startOfLineIndex = 0; + uint32_t endOfLineIndex = 0; + + // Number of lines. + uint32_t lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; + + // Max main dimension of all the lines. + float maxLineMainDim = 0; + YGCollectFlexItemsRowValues collectedFlexItemsValues; + for (; endOfLineIndex < childCount; + lineCount++, startOfLineIndex = endOfLineIndex) { + collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues( + node, + parentDirection, + mainAxisParentSize, + availableInnerWidth, + availableInnerMainDim, + startOfLineIndex, + lineCount); + endOfLineIndex = collectedFlexItemsValues.endOfLineIndex; + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + const bool canSkipFlex = + !performLayout && measureModeCrossDim == YGMeasureModeExactly; + + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + + bool sizeBasedOnContent = false; + // If we don't measure with exact main dimension we want to ensure we don't violate min and max + if (measureModeMainDim != YGMeasureModeExactly) { + if (!YGFloatIsUndefined(minInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine < + minInnerMainDim) { + availableInnerMainDim = minInnerMainDim; + } else if ( + !YGFloatIsUndefined(maxInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine > + maxInnerMainDim) { + availableInnerMainDim = maxInnerMainDim; + } else { + if (!node->getConfig()->useLegacyStretchBehaviour && + (collectedFlexItemsValues.totalFlexGrowFactors == 0 || + node->resolveFlexGrow() == 0)) { + // If we don't have any children to flex or we can't flex the node + // itself, space we've used is all space we need. Root node also + // should be shrunk to minimum + availableInnerMainDim = + collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } + + if (node->getConfig()->useLegacyStretchBehaviour) { + node->setLayoutDidUseLegacyFlag(true); + } + sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour; + } + } + + if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) { + collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim - + collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its + // content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + collectedFlexItemsValues.remainingFreeSpace = + -collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } + + if (!canSkipFlex) { + YGResolveFlexibleLength( + node, + collectedFlexItemsValues, + mainAxis, + crossAxis, + mainAxisParentSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config); + } + + node->setLayoutHadOverflow( + node->getLayout().hadOverflow | + (collectedFlexItemsValues.remainingFreeSpace < 0)); + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main + // axis. + // Their dimensions are also set in the cross axis with the exception of + // items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + YGJustifyMainAxis( + node, + collectedFlexItemsValues, + startOfLineIndex, + mainAxis, + crossAxis, + measureModeMainDim, + measureModeCrossDim, + mainAxisParentSize, + parentWidth, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + performLayout); + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == YGMeasureModeUndefined || + measureModeCrossDim == YGMeasureModeAtMost) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = + YGNodeBoundAxis( + node, + crossAxis, + collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross; + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) { + collectedFlexItemsValues.crossDim = availableInnerCrossDim; + } + + // Clamp to the min/max size specified on the container. + collectedFlexItemsValues.crossDim = + YGNodeBoundAxis( + node, + crossAxis, + collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth) - + paddingAndBorderAxisCross; + + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().display == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType == YGPositionTypeAbsolute) { + // If the child is absolutely positioned and has a + // top/left/bottom/right set, override + // all the previously computed positions to set it correctly. + const bool isChildLeadingPosDefined = + child->isLeadingPositionDefined(crossAxis); + if (isChildLeadingPosDefined) { + child->setLayoutPosition( + child->getLeadingPosition(crossAxis, availableInnerCrossDim) + + node->getLeadingBorder(crossAxis) + + child->getLeadingMargin(crossAxis, availableInnerWidth), + pos[crossAxis]); + } + // If leading position is not defined or calculations result in Nan, default to border + margin + if (!isChildLeadingPosDefined || + YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) { + child->setLayoutPosition( + node->getLeadingBorder(crossAxis) + + child->getLeadingMargin(crossAxis, availableInnerWidth), + pos[crossAxis]); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross + // axis + const YGAlign alignItem = YGNodeAlignItem(node, child); + + // If the child uses align stretch, we need to lay it out one more + // time, this time + // forcing the cross-axis size to be the computed cross size for the + // current line. + if (alignItem == YGAlignStretch && + child->marginLeadingValue(crossAxis).unit != YGUnitAuto && + child->marginTrailingValue(crossAxis).unit != YGUnitAuto) { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { + float childMainSize = + child->getLayout().measuredDimensions[dim[mainAxis]]; + float childCrossSize = + !YGFloatIsUndefined(child->getStyle().aspectRatio) + ? ((child->getMarginForAxis(crossAxis, availableInnerWidth) + + (isMainAxisRow + ? childMainSize / child->getStyle().aspectRatio + : childMainSize * child->getStyle().aspectRatio))) + : collectedFlexItemsValues.crossDim; + + childMainSize += + child->getMarginForAxis(mainAxis, availableInnerWidth); + + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly; + YGConstrainMaxSizeForMode(child, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize); + YGConstrainMaxSizeForMode(child, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize); + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const YGMeasureMode childWidthMeasureMode = + YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined + : YGMeasureModeExactly; + const YGMeasureMode childHeightMeasureMode = + YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined + : YGMeasureModeExactly; + + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + true, + "stretch", + config); + } + } else { + const float remainingCrossDim = containerCrossAxis - + YGNodeDimWithMargin(child, crossAxis, availableInnerWidth); + + if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto && + child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { + leadingCrossDim += fmaxf(0.0f, remainingCrossDim / 2); + } else if ( + child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { + // No-Op + } else if ( + child->marginLeadingValue(crossAxis).unit == YGUnitAuto) { + leadingCrossDim += fmaxf(0.0f, remainingCrossDim); + } else if (alignItem == YGAlignFlexStart) { + // No-Op + } else if (alignItem == YGAlignCenter) { + leadingCrossDim += remainingCrossDim / 2; + } else { + leadingCrossDim += remainingCrossDim; + } + } + // And we apply the position + child->setLayoutPosition( + child->getLayout().position[pos[crossAxis]] + totalLineCrossDim + + leadingCrossDim, + pos[crossAxis]); + } + } + } + + totalLineCrossDim += collectedFlexItemsValues.crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, collectedFlexItemsValues.mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) && + !YGFloatIsUndefined(availableInnerCrossDim)) { + const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; + + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + + switch (node->getStyle().alignContent) { + case YGAlignFlexEnd: + currentLead += remainingAlignContentDim; + break; + case YGAlignCenter: + currentLead += remainingAlignContentDim / 2; + break; + case YGAlignStretch: + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = remainingAlignContentDim / lineCount; + } + break; + case YGAlignSpaceAround: + if (availableInnerCrossDim > totalLineCrossDim) { + currentLead += remainingAlignContentDim / (2 * lineCount); + if (lineCount > 1) { + crossDimLead = remainingAlignContentDim / lineCount; + } + } else { + currentLead += remainingAlignContentDim / 2; + } + break; + case YGAlignSpaceBetween: + if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) { + crossDimLead = remainingAlignContentDim / (lineCount - 1); + } + break; + case YGAlignAuto: + case YGAlignFlexStart: + case YGAlignBaseline: + break; + } + + uint32_t endIndex = 0; + for (uint32_t i = 0; i < lineCount; i++) { + const uint32_t startIndex = endIndex; + uint32_t ii; + + // compute the line's height and find the endIndex + float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + for (ii = startIndex; ii < childCount; ii++) { + const YGNodeRef child = node->getChild(ii); + if (child->getStyle().display == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType == YGPositionTypeRelative) { + if (child->getLineIndex() != i) { + break; + } + if (YGNodeIsLayoutDimDefined(child, crossAxis)) { + lineHeight = fmaxf( + lineHeight, + child->getLayout().measuredDimensions[dim[crossAxis]] + + child->getMarginForAxis(crossAxis, availableInnerWidth)); + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + const float ascent = YGBaseline(child) + + child->getLeadingMargin( + YGFlexDirectionColumn, availableInnerWidth); + const float descent = + child->getLayout().measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis( + YGFlexDirectionColumn, availableInnerWidth) - + ascent; + maxAscentForCurrentLine = fmaxf(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = fmaxf(maxDescentForCurrentLine, descent); + lineHeight = fmaxf(lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } + } + } + endIndex = ii; + lineHeight += crossDimLead; + + if (performLayout) { + for (ii = startIndex; ii < endIndex; ii++) { + const YGNodeRef child = node->getChild(ii); + if (child->getStyle().display == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType == YGPositionTypeRelative) { + switch (YGNodeAlignItem(node, child)) { + case YGAlignFlexStart: { + child->setLayoutPosition( + currentLead + + child->getLeadingMargin(crossAxis, availableInnerWidth), + pos[crossAxis]); + break; + } + case YGAlignFlexEnd: { + child->setLayoutPosition( + currentLead + lineHeight - + child->getTrailingMargin( + crossAxis, availableInnerWidth) - + child->getLayout().measuredDimensions[dim[crossAxis]], + pos[crossAxis]); + break; + } + case YGAlignCenter: { + float childHeight = + child->getLayout().measuredDimensions[dim[crossAxis]]; + + child->setLayoutPosition( + currentLead + (lineHeight - childHeight) / 2, + pos[crossAxis]); + break; + } + case YGAlignStretch: { + child->setLayoutPosition( + currentLead + + child->getLeadingMargin(crossAxis, availableInnerWidth), + pos[crossAxis]); + + // Remeasure child with the line height as it as been only measured with the + // parents height yet. + if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) { + const float childWidth = isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionWidth] + + child->getMarginForAxis(mainAxis, availableInnerWidth)) + : lineHeight; + + const float childHeight = !isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis( + crossAxis, availableInnerWidth)) + : lineHeight; + + if (!(YGFloatsEqual( + childWidth, + child->getLayout() + .measuredDimensions[YGDimensionWidth]) && + YGFloatsEqual( + childHeight, + child->getLayout() + .measuredDimensions[YGDimensionHeight]))) { + YGLayoutNodeInternal(child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + availableInnerWidth, + availableInnerHeight, + true, + "multiline-stretch", + config); + } + } + break; + } + case YGAlignBaseline: { + child->setLayoutPosition( + currentLead + maxAscentForCurrentLine - YGBaseline(child) + + child->getLeadingPosition( + YGFlexDirectionColumn, availableInnerCrossDim), + YGEdgeTop); + + break; + } + case YGAlignAuto: + case YGAlignSpaceBetween: + case YGAlignSpaceAround: + break; + } + } + } + } + + currentLead += lineHeight; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + availableWidth - marginAxisRow, + parentWidth, + parentWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + parentHeight, + parentWidth), + YGDimensionHeight); + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == YGMeasureModeUndefined || + (node->getStyle().overflow != YGOverflowScroll && + measureModeMainDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, mainAxis, maxLineMainDim, mainAxisParentSize, parentWidth), + dim[mainAxis]); + + } else if ( + measureModeMainDim == YGMeasureModeAtMost && + node->getStyle().overflow == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + fmaxf( + fminf( + availableInnerMainDim + paddingAndBorderAxisMain, + YGNodeBoundAxisWithinMinAndMax( + node, mainAxis, maxLineMainDim, mainAxisParentSize)), + paddingAndBorderAxisMain), + dim[mainAxis]); + } + + if (measureModeCrossDim == YGMeasureModeUndefined || + (node->getStyle().overflow != YGOverflowScroll && + measureModeCrossDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisParentSize, + parentWidth), + dim[crossAxis]); + + } else if ( + measureModeCrossDim == YGMeasureModeAtMost && + node->getStyle().overflow == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + fmaxf( + fminf( + availableInnerCrossDim + paddingAndBorderAxisCross, + YGNodeBoundAxisWithinMinAndMax( + node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisParentSize)), + paddingAndBorderAxisCross), + dim[crossAxis]); + } + + // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse. + if (performLayout && node->getStyle().flexWrap == YGWrapWrapReverse) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->getStyle().positionType == YGPositionTypeRelative) { + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().position[pos[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]], + pos[crossAxis]); + } + } + } + + if (performLayout) { + // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN + for (auto child : node->getChildren()) { + if (child->getStyle().positionType != YGPositionTypeAbsolute) { + continue; + } + YGNodeAbsoluteLayoutChild( + node, + child, + availableInnerWidth, + isMainAxisRow ? measureModeMainDim : measureModeCrossDim, + availableInnerHeight, + direction, + config); + } + + // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN + const bool needsMainTrailingPos = + mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse; + const bool needsCrossTrailingPos = + crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse; + + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().display == YGDisplayNone) { + continue; + } + if (needsMainTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, crossAxis); + } + } + } + } +} + +uint32_t gDepth = 0; +bool gPrintTree = false; +bool gPrintChanges = false; +bool gPrintSkips = false; + +static const char *spacer = " "; + +static const char *YGSpacer(const unsigned long level) { + const size_t spacerLen = strlen(spacer); + if (level > spacerLen) { + return &spacer[0]; + } else { + return &spacer[spacerLen - level]; + } +} + +static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) { + const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"}; + const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED", + "LAY_EXACTLY", + "LAY_AT_" + "MOST"}; + + if (mode >= YGMeasureModeCount) { + return ""; + } + + return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode]; +} + +static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode, + float size, + float lastComputedSize) { + return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize); +} + +static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastComputedSize) { + return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined && + (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize)); +} + +static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastSize, + float lastComputedSize) { + return lastSizeMode == YGMeasureModeAtMost && sizeMode == YGMeasureModeAtMost && + lastSize > size && (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); +} + +float YGRoundValueToPixelGrid(const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + float scaledValue = value * pointScaleFactor; + float fractial = fmodf(scaledValue, 1.0); + if (YGFloatsEqual(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (YGFloatsEqual(fractial, 1.0)) { + scaledValue = scaledValue - fractial + 1.0; + } else if (forceCeil) { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0f; + } else if (forceFloor) { + scaledValue = scaledValue - fractial; + } else { + // Finally we just round the value + scaledValue = scaledValue - fractial + + (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f) ? 1.0f : 0.0f); + } + return scaledValue / pointScaleFactor; +} + +bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, + const float width, + const YGMeasureMode heightMode, + const float height, + const YGMeasureMode lastWidthMode, + const float lastWidth, + const YGMeasureMode lastHeightMode, + const float lastHeight, + const float lastComputedWidth, + const float lastComputedHeight, + const float marginRow, + const float marginColumn, + const YGConfigRef config) { + if (lastComputedHeight < 0 || lastComputedWidth < 0) { + return false; + } + bool useRoundedComparison = + config != nullptr && config->pointScaleFactor != 0; + const float effectiveWidth = + useRoundedComparison ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false) + : width; + const float effectiveHeight = + useRoundedComparison ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false) + : height; + const float effectiveLastWidth = + useRoundedComparison + ? YGRoundValueToPixelGrid(lastWidth, config->pointScaleFactor, false, false) + : lastWidth; + const float effectiveLastHeight = + useRoundedComparison + ? YGRoundValueToPixelGrid(lastHeight, config->pointScaleFactor, false, false) + : lastHeight; + + const bool hasSameWidthSpec = + lastWidthMode == widthMode && YGFloatsEqual(effectiveLastWidth, effectiveWidth); + const bool hasSameHeightSpec = + lastHeightMode == heightMode && YGFloatsEqual(effectiveLastHeight, effectiveHeight); + + const bool widthIsCompatible = + hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode, + width - marginRow, + lastComputedWidth) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode, + width - marginRow, + lastWidthMode, + lastComputedWidth) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth); + + const bool heightIsCompatible = + hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode, + height - marginColumn, + lastComputedHeight) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode, + height - marginColumn, + lastHeightMode, + lastComputedHeight) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight); + + return widthIsCompatible && heightIsCompatible; +} + +// +// This is a wrapper around the YGNodelayoutImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as YGNodelayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool YGLayoutNodeInternal(const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection parentDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float parentWidth, + const float parentHeight, + const bool performLayout, + const char *reason, + const YGConfigRef config) { + YGLayout* layout = &node->getLayout(); + + gDepth++; + + const bool needToVisitNode = + (node->isDirty() && layout->generationCount != gCurrentGenerationCount) || + layout->lastParentDirection != parentDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->nextCachedMeasurementsIndex = 0; + layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.computedWidth = -1; + layout->cachedLayout.computedHeight = -1; + } + + YGCachedMeasurement* cachedResults = nullptr; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the + // positions + // and dimensions for nodes in the subtree. The algorithm assumes that each + // node + // gets layed out a maximum of one time per tree layout, but multiple + // measurements + // may be required to resolve all of the flex dimensions. + // We handle nodes with measure functions specially here because they are the + // most + // expensive to measure, so it's worth avoiding redundant measurements if at + // all possible. + if (node->getMeasure() != nullptr) { + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + + // First, try to use the layout cache. + if (YGNodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedLayout.widthMeasureMode, + layout->cachedLayout.availableWidth, + layout->cachedLayout.heightMeasureMode, + layout->cachedLayout.availableHeight, + layout->cachedLayout.computedWidth, + layout->cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedLayout; + } else { + // Try to use the measurement cache. + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGNodeCanUseCachedMeasurement(widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedMeasurements[i].widthMeasureMode, + layout->cachedMeasurements[i].availableWidth, + layout->cachedMeasurements[i].heightMeasureMode, + layout->cachedMeasurements[i].availableHeight, + layout->cachedMeasurements[i].computedWidth, + layout->cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { + if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) && + YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) && + layout->cachedLayout.widthMeasureMode == widthMeasureMode && + layout->cachedLayout.heightMeasureMode == heightMeasureMode) { + cachedResults = &layout->cachedLayout; + } + } else { + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) && + YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) && + layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != nullptr) { + layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth; + layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight; + + if (gPrintChanges && gPrintSkips) { + YGLog(node, YGLogLevelVerbose, "%s%d.{[skipped] ", YGSpacer(gDepth), gDepth); + if (node->getPrintFunc() != nullptr) { + node->getPrintFunc()(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + cachedResults->computedWidth, + cachedResults->computedHeight, + reason); + } + } else { + if (gPrintChanges) { + YGLog( + node, + YGLogLevelVerbose, + "%s%d.{%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + if (node->getPrintFunc() != nullptr) { + node->getPrintFunc()(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, aw: %f ah: %f %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + reason); + } + + YGNodelayoutImpl(node, + availableWidth, + availableHeight, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + performLayout, + config); + + if (gPrintChanges) { + YGLog( + node, + YGLogLevelVerbose, + "%s%d.}%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + if (node->getPrintFunc() != nullptr) { + node->getPrintFunc()(node); + } + YGLog( + node, + YGLogLevelVerbose, + "wm: %s, hm: %s, d: (%f, %f) %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + layout->measuredDimensions[YGDimensionWidth], + layout->measuredDimensions[YGDimensionHeight], + reason); + } + + layout->lastParentDirection = parentDirection; + + if (cachedResults == nullptr) { + if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) { + if (gPrintChanges) { + YGLog(node, YGLogLevelVerbose, "Out of cache entries!\n"); + } + layout->nextCachedMeasurementsIndex = 0; + } + + YGCachedMeasurement *newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = &layout->cachedLayout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex]; + layout->nextCachedMeasurementsIndex++; + } + + newCacheEntry->availableWidth = availableWidth; + newCacheEntry->availableHeight = availableHeight; + newCacheEntry->widthMeasureMode = widthMeasureMode; + newCacheEntry->heightMeasureMode = heightMeasureMode; + newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth]; + newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight]; + } + } + + if (performLayout) { + node->setLayoutDimension( + node->getLayout().measuredDimensions[YGDimensionWidth], + YGDimensionWidth); + node->setLayoutDimension( + node->getLayout().measuredDimensions[YGDimensionHeight], + YGDimensionHeight); + + node->setHasNewLayout(true); + node->setDirty(false); + } + + gDepth--; + layout->generationCount = gCurrentGenerationCount; + return (needToVisitNode || cachedResults == nullptr); +} + +void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) { + YGAssertWithConfig(config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero"); + + // We store points for Pixel as we will use it for rounding + if (pixelsInPoint == 0.0f) { + // Zero is used to skip rounding + config->pointScaleFactor = 0.0f; + } else { + config->pointScaleFactor = pixelsInPoint; + } +} + +static void YGRoundToPixelGrid(const YGNodeRef node, + const float pointScaleFactor, + const float absoluteLeft, + const float absoluteTop) { + if (pointScaleFactor == 0.0f) { + return; + } + + const float nodeLeft = node->getLayout().position[YGEdgeLeft]; + const float nodeTop = node->getLayout().position[YGEdgeTop]; + + const float nodeWidth = node->getLayout().dimensions[YGDimensionWidth]; + const float nodeHeight = node->getLayout().dimensions[YGDimensionHeight]; + + const float absoluteNodeLeft = absoluteLeft + nodeLeft; + const float absoluteNodeTop = absoluteTop + nodeTop; + + const float absoluteNodeRight = absoluteNodeLeft + nodeWidth; + const float absoluteNodeBottom = absoluteNodeTop + nodeHeight; + + // If a node has a custom measure function we never want to round down its size as this could + // lead to unwanted text truncation. + const bool textRounding = node->getNodeType() == YGNodeTypeText; + + node->setLayoutPosition( + YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding), + YGEdgeLeft); + + node->setLayoutPosition( + YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding), + YGEdgeTop); + + // We multiply dimension by scale factor and if the result is close to the whole number, we don't + // have any fraction + // To verify if the result is close to whole number we want to check both floor and ceil numbers + const bool hasFractionalWidth = !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0); + const bool hasFractionalHeight = !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0); + + node->setLayoutDimension( + YGRoundValueToPixelGrid( + absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + YGRoundValueToPixelGrid( + absoluteNodeLeft, pointScaleFactor, false, textRounding), + YGDimensionWidth); + + node->setLayoutDimension( + YGRoundValueToPixelGrid( + absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + YGRoundValueToPixelGrid( + absoluteNodeTop, pointScaleFactor, false, textRounding), + YGDimensionHeight); + + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + YGRoundToPixelGrid(YGNodeGetChild(node, i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop); + } +} + +void YGNodeCalculateLayout(const YGNodeRef node, + const float parentWidth, + const float parentHeight, + const YGDirection parentDirection) { + // Increment the generation count. This will force the recursive routine to + // visit + // all dirty nodes at least once. Subsequent visits will be skipped if the + // input + // parameters don't change. + gCurrentGenerationCount++; + node->resolveDimension(); + float width = YGUndefined; + YGMeasureMode widthMeasureMode = YGMeasureModeUndefined; + if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, parentWidth)) { + width = + YGResolveValue( + node->getResolvedDimension(dim[YGFlexDirectionRow]), parentWidth) + + node->getMarginForAxis(YGFlexDirectionRow, parentWidth); + widthMeasureMode = YGMeasureModeExactly; + } else if ( + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], parentWidth) >= + 0.0f) { + width = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], parentWidth); + widthMeasureMode = YGMeasureModeAtMost; + } else { + width = parentWidth; + widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined : YGMeasureModeExactly; + } + + float height = YGUndefined; + YGMeasureMode heightMeasureMode = YGMeasureModeUndefined; + if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, parentHeight)) { + height = YGResolveValue( + node->getResolvedDimension(dim[YGFlexDirectionColumn]), + parentHeight) + + node->getMarginForAxis(YGFlexDirectionColumn, parentWidth); + heightMeasureMode = YGMeasureModeExactly; + } else if ( + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], parentHeight) >= + 0.0f) { + height = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], parentHeight); + heightMeasureMode = YGMeasureModeAtMost; + } else { + height = parentHeight; + heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined : YGMeasureModeExactly; + } + + if (YGLayoutNodeInternal( + node, + width, + height, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + true, + "initial", + node->getConfig())) { + node->setPosition( + node->getLayout().direction, parentWidth, parentHeight, parentWidth); + YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f); + + if (gPrintTree) { + YGNodePrint( + node, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } + } + + // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we + // aren't sure whether client's of yoga have gotten rid off this flag or not. + // So logging this in YGLayout would help to find out the call sites depending + // on this flag. This check would be removed once we are sure no one is + // dependent on this flag anymore. The flag + // `shouldDiffLayoutWithoutLegacyStretchBehaviour` in YGConfig will help to + // run experiments. + if (node->getConfig()->shouldDiffLayoutWithoutLegacyStretchBehaviour && + node->didUseLegacyFlag()) { + const YGNodeRef originalNode = YGNodeDeepClone(node); + originalNode->resolveDimension(); + // Recursively mark nodes as dirty + originalNode->markDirtyAndPropogateDownwards(); + gCurrentGenerationCount++; + // Rerun the layout, and calculate the diff + originalNode->setAndPropogateUseLegacyFlag(false); + if (YGLayoutNodeInternal( + originalNode, + width, + height, + parentDirection, + widthMeasureMode, + heightMeasureMode, + parentWidth, + parentHeight, + true, + "initial", + originalNode->getConfig())) { + originalNode->setPosition( + originalNode->getLayout().direction, + parentWidth, + parentHeight, + parentWidth); + YGRoundToPixelGrid( + originalNode, + originalNode->getConfig()->pointScaleFactor, + 0.0f, + 0.0f); + + // Set whether the two layouts are different or not. + node->setLayoutDoesLegacyFlagAffectsLayout( + !originalNode->isLayoutTreeEqualToNode(*node)); + + if (gPrintTree) { + YGNodePrint( + originalNode, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } + } + YGConfigFreeRecursive(originalNode); + YGNodeFreeRecursive(originalNode); + } +} + +void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { + if (logger != nullptr) { + config->logger = logger; + } else { +#ifdef ANDROID + config->logger = &YGAndroidLog; +#else + config->logger = &YGDefaultLog; +#endif + } +} + +static void YGVLog(const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char *format, + va_list args) { + const YGConfigRef logConfig = config != nullptr ? config : &gYGConfigDefaults; + logConfig->logger(logConfig, node, level, format, args); + + if (level == YGLogLevelFatal) { + abort(); + } +} + +void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...) { + va_list args; + va_start(args, format); + YGVLog(config, nullptr, level, format, args); + va_end(args); +} + +void YGLog(const YGNodeRef node, YGLogLevel level, const char *format, ...) { + va_list args; + va_start(args, format); + YGVLog( + node == nullptr ? nullptr : node->getConfig(), node, level, format, args); + va_end(args); +} + +void YGAssert(const bool condition, const char *message) { + if (!condition) { + YGLog(nullptr, YGLogLevelFatal, "%s\n", message); + } +} + +void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message) { + if (!condition) { + YGLog(node, YGLogLevelFatal, "%s\n", message); + } +} + +void YGAssertWithConfig(const YGConfigRef config, const bool condition, const char *message) { + if (!condition) { + YGLogWithConfig(config, YGLogLevelFatal, "%s\n", message); + } +} + +void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + config->experimentalFeatures[feature] = enabled; +} + +inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, + const YGExperimentalFeature feature) { + return config->experimentalFeatures[feature]; +} + +void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) { + config->useWebDefaults = enabled; +} + +void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config, + const bool useLegacyStretchBehaviour) { + config->useLegacyStretchBehaviour = useLegacyStretchBehaviour; +} + +bool YGConfigGetUseWebDefaults(const YGConfigRef config) { + return config->useWebDefaults; +} + +void YGConfigSetContext(const YGConfigRef config, void *context) { + config->context = context; +} + +void *YGConfigGetContext(const YGConfigRef config) { + return config->context; +} + +void YGConfigSetNodeClonedFunc(const YGConfigRef config, const YGNodeClonedFunc callback) { + config->cloneNodeCallback = callback; +} diff --git a/Sources/yoga/Yoga.h b/Sources/yoga/Yoga.h index c3d3260d..689f9f3e 100644 --- a/Sources/yoga/Yoga.h +++ b/Sources/yoga/Yoga.h @@ -1,10 +1,8 @@ /** * 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. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. */ #pragma once @@ -43,32 +41,35 @@ typedef struct YGValue { YGUnit unit; } YGValue; -static const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; -static const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; +extern const YGValue YGValueUndefined; +extern const YGValue YGValueAuto; typedef struct YGConfig *YGConfigRef; -typedef struct YGNode *YGNodeRef; + +typedef struct YGNode* YGNodeRef; + typedef YGSize (*YGMeasureFunc)(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode); typedef float (*YGBaselineFunc)(YGNodeRef node, const float width, const float height); +typedef void (*YGDirtiedFunc)(YGNodeRef node); typedef void (*YGPrintFunc)(YGNodeRef node); typedef int (*YGLogger)(const YGConfigRef config, const YGNodeRef node, YGLogLevel level, const char *format, va_list args); - -typedef void *(*YGMalloc)(size_t size); -typedef void *(*YGCalloc)(size_t count, size_t size); -typedef void *(*YGRealloc)(void *ptr, size_t size); -typedef void (*YGFree)(void *ptr); +typedef void (*YGNodeClonedFunc)(YGNodeRef oldNode, + YGNodeRef newNode, + YGNodeRef parent, + int childIndex); // YGNode WIN_EXPORT YGNodeRef YGNodeNew(void); WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config); +WIN_EXPORT YGNodeRef YGNodeClone(const YGNodeRef node); WIN_EXPORT void YGNodeFree(const YGNodeRef node); WIN_EXPORT void YGNodeFreeRecursive(const YGNodeRef node); WIN_EXPORT void YGNodeReset(const YGNodeRef node); @@ -78,6 +79,7 @@ WIN_EXPORT void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index); WIN_EXPORT void YGNodeRemoveChild(const YGNodeRef node, const YGNodeRef child); +WIN_EXPORT void YGNodeRemoveAllChildren(const YGNodeRef node); WIN_EXPORT YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index); WIN_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node); WIN_EXPORT uint32_t YGNodeGetChildCount(const YGNodeRef node); @@ -94,7 +96,10 @@ WIN_EXPORT void YGNodeCalculateLayout(const YGNodeRef node, // depends on information not known to YG they must perform this dirty // marking manually. WIN_EXPORT void YGNodeMarkDirty(const YGNodeRef node); -WIN_EXPORT bool YGNodeIsDirty(const YGNodeRef node); + +// This function marks the current node and all its descendants as dirty. This function is added to test yoga benchmarks. +// This function is not expected to be used in production as calling `YGCalculateLayout` will cause the recalculation of each and every node. +WIN_EXPORT void YGNodeMarkDirtyAndPropogateToDescendants(const YGNodeRef node); WIN_EXPORT void YGNodePrint(const YGNodeRef node, const YGPrintOptions options); @@ -112,7 +117,7 @@ WIN_EXPORT bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode, const float lastComputedHeight, const float marginRow, const float marginColumn, - YGConfigRef config); + const YGConfigRef config); WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode); @@ -157,12 +162,22 @@ WIN_EXPORT void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode #define YG_NODE_LAYOUT_EDGE_PROPERTY(type, name) \ WIN_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge); -YG_NODE_PROPERTY(void *, Context, context); -YG_NODE_PROPERTY(YGMeasureFunc, MeasureFunc, measureFunc); -YG_NODE_PROPERTY(YGBaselineFunc, BaselineFunc, baselineFunc) -YG_NODE_PROPERTY(YGPrintFunc, PrintFunc, printFunc); -YG_NODE_PROPERTY(bool, HasNewLayout, hasNewLayout); -YG_NODE_PROPERTY(YGNodeType, NodeType, nodeType); +void* YGNodeGetContext(YGNodeRef node); +void YGNodeSetContext(YGNodeRef node, void* context); +YGMeasureFunc YGNodeGetMeasureFunc(YGNodeRef node); +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); +YGBaselineFunc YGNodeGetBaselineFunc(YGNodeRef node); +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc); +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node); +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc); +YGPrintFunc YGNodeGetPrintFunc(YGNodeRef node); +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc); +bool YGNodeGetHasNewLayout(YGNodeRef node); +void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); +YGNodeType YGNodeGetNodeType(YGNodeRef node); +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); +bool YGNodeIsDirty(YGNodeRef node); +bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node); YG_NODE_STYLE_PROPERTY(YGDirection, Direction, direction); YG_NODE_STYLE_PROPERTY(YGFlexDirection, FlexDirection, flexDirection); @@ -264,13 +279,19 @@ WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config, WIN_EXPORT void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled); WIN_EXPORT bool YGConfigGetUseWebDefaults(const YGConfigRef config); +WIN_EXPORT void YGConfigSetNodeClonedFunc(const YGConfigRef config, + const YGNodeClonedFunc callback); + // Export only for C# WIN_EXPORT YGConfigRef YGConfigGetDefault(void); WIN_EXPORT void YGConfigSetContext(const YGConfigRef config, void *context); WIN_EXPORT void *YGConfigGetContext(const YGConfigRef config); -WIN_EXPORT void -YGSetMemoryFuncs(YGMalloc ygmalloc, YGCalloc yccalloc, YGRealloc ygrealloc, YGFree ygfree); +WIN_EXPORT float YGRoundValueToPixelGrid( + const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); YG_EXTERN_C_END diff --git a/lib/fb/BUCK b/lib/fb/BUCK new file mode 100644 index 00000000..50077d2e --- /dev/null +++ b/lib/fb/BUCK @@ -0,0 +1,44 @@ +# Copyright (c) 2014-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +include_defs("//yoga_defs.bzl") + +prebuilt_cxx_library( + name = "ndklog", + exported_platform_linker_flags = [ + ( + "^android.*", + ["-llog"], + ), + ], + header_only = True, + visibility = YOGA_ROOTS, +) + +cxx_library( + name = "fbjni", + srcs = glob(["src/main/cpp/**/*.cpp"]), + header_namespace = "", + exported_headers = subdir_glob([("src/main/cpp/include", "**/*.h")]), + compiler_flags = [ + "-DLOG_TAG=\"libfb\"", + "-DDISABLE_CPUCAP", + "-DDISABLE_XPLAT", + "-DHAVE_POSIX_CLOCKS", + "-fno-omit-frame-pointer", + "-fexceptions", + "-frtti", + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-std=c++11", + ], + visibility = ["PUBLIC"], + deps = [ + ":ndklog", + FBJNI_JAVA_TARGET, + JNI_TARGET, + ], +) diff --git a/lib/fb/build.gradle b/lib/fb/build.gradle new file mode 100644 index 00000000..7a419b0b --- /dev/null +++ b/lib/fb/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + + ndk { + abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a' + } + + externalNativeBuild { + cmake { + arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static' + } + } + } + + externalNativeBuild { + cmake { + path 'src/main/cpp/CMakeLists.txt' + } + } + + dependencies { + implementation 'com.facebook.soloader:soloader:0.2.0' + compileOnly 'com.google.code.findbugs:jsr305:3.0.1' + compileOnly project(':yoga:proguard-annotations') + } +} diff --git a/lib/fb/src/main/AndroidManifest.xml b/lib/fb/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fb03ab18 --- /dev/null +++ b/lib/fb/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/lib/fb/src/main/cpp/CMakeLists.txt b/lib/fb/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..59cc2b72 --- /dev/null +++ b/lib/fb/src/main/cpp/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright (c) 2014-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. +# + +cmake_minimum_required(VERSION 3.4.1) + +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fno-omit-frame-pointer + -fexceptions + -Wall + -std=c++11 + -DDISABLE_CPUCAP + -DDISABLE_XPLAT) + +file(GLOB fb_SRC + *.cpp + jni/*.cpp + lyra/*.cpp) + +add_library(fb SHARED + ${fb_SRC}) + +target_include_directories(fb PRIVATE + include) + +target_link_libraries(fb android log) diff --git a/lib/fb/src/main/cpp/Doxyfile b/lib/fb/src/main/cpp/Doxyfile new file mode 100644 index 00000000..b44118dd --- /dev/null +++ b/lib/fb/src/main/cpp/Doxyfile @@ -0,0 +1,15 @@ +PROJECT_NAME = "Facebook Android Support" +JAVADOC_AUTOBRIEF = YES +EXTRACT_ALL = YES +RECURSIVE = YES +EXCLUDE = tests +EXCLUDE_PATTERNS = *.cpp +GENERATE_HTML = YES +GENERATE_LATEX = NO +ENABLE_PREPROCESSING = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_SCOPE_NAMES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_UNDOC_CLASSES = YES +SHOW_INCLUDE_FILES = NO +#ENABLED_SECTIONS = INTERNAL diff --git a/lib/fb/src/main/cpp/assert.cpp b/lib/fb/src/main/cpp/assert.cpp new file mode 100644 index 00000000..d39c29e4 --- /dev/null +++ b/lib/fb/src/main/cpp/assert.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include +#include + +#include +#include + +namespace facebook { + +#define ASSERT_BUF_SIZE 4096 +static char sAssertBuf[ASSERT_BUF_SIZE]; +static AssertHandler gAssertHandler; + +void assertInternal(const char* formatstr ...) { + va_list va_args; + va_start(va_args, formatstr); + vsnprintf(sAssertBuf, sizeof(sAssertBuf), formatstr, va_args); + va_end(va_args); + if (gAssertHandler != NULL) { + gAssertHandler(sAssertBuf); + } + FBLOG(LOG_FATAL, "fbassert", "%s", sAssertBuf); + // crash at this specific address so that we can find our crashes easier + *(int*)0xdeadb00c = 0; + // let the compiler know we won't reach the end of the function + __builtin_unreachable(); +} + +void setAssertHandler(AssertHandler assertHandler) { + gAssertHandler = assertHandler; +} + +} // namespace facebook diff --git a/lib/fb/src/main/cpp/include/fb/ALog.h b/lib/fb/src/main/cpp/include/fb/ALog.h new file mode 100644 index 00000000..5f231cb7 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/ALog.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** @file ALog.h + * + * Very simple android only logging. Define LOG_TAG to enable the macros. + */ + +#pragma once + +#ifdef __ANDROID__ + +#include + +namespace facebook { +namespace alog { + +template +inline void log(int level, const char* tag, const char* msg, ARGS... args) noexcept { + __android_log_print(level, tag, msg, args...); +} + +template +inline void log(int level, const char* tag, const char* msg) noexcept { + __android_log_write(level, tag, msg); +} + +template +inline void logv(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_VERBOSE, tag, msg, args...); +} + +template +inline void logd(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_DEBUG, tag, msg, args...); +} + +template +inline void logi(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_INFO, tag, msg, args...); +} + +template +inline void logw(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_WARN, tag, msg, args...); +} + +template +inline void loge(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_ERROR, tag, msg, args...); +} + +template +inline void logf(const char* tag, const char* msg, ARGS... args) noexcept { + log(ANDROID_LOG_FATAL, tag, msg, args...); +} + + +#ifdef LOG_TAG +# define ALOGV(...) ::facebook::alog::logv(LOG_TAG, __VA_ARGS__) +# define ALOGD(...) ::facebook::alog::logd(LOG_TAG, __VA_ARGS__) +# define ALOGI(...) ::facebook::alog::logi(LOG_TAG, __VA_ARGS__) +# define ALOGW(...) ::facebook::alog::logw(LOG_TAG, __VA_ARGS__) +# define ALOGE(...) ::facebook::alog::loge(LOG_TAG, __VA_ARGS__) +# define ALOGF(...) ::facebook::alog::logf(LOG_TAG, __VA_ARGS__) +#endif + +}} + +#else +# define ALOGV(...) ((void)0) +# define ALOGD(...) ((void)0) +# define ALOGI(...) ((void)0) +# define ALOGW(...) ((void)0) +# define ALOGE(...) ((void)0) +# define ALOGF(...) ((void)0) +#endif diff --git a/lib/fb/src/main/cpp/include/fb/Countable.h b/lib/fb/src/main/cpp/include/fb/Countable.h new file mode 100644 index 00000000..1fafba36 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/Countable.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace facebook { + +class Countable : public noncopyable, public nonmovable { +public: + // RefPtr expects refcount to start at 0 + Countable() : m_refcount(0) {} + virtual ~Countable() + { + FBASSERT(m_refcount == 0); + } + +private: + void ref() { + ++m_refcount; + } + + void unref() { + if (0 == --m_refcount) { + delete this; + } + } + + bool hasOnlyOneRef() const { + return m_refcount == 1; + } + + template friend class RefPtr; + std::atomic m_refcount; +}; + +} diff --git a/lib/fb/src/main/cpp/include/fb/Doxyfile b/lib/fb/src/main/cpp/include/fb/Doxyfile new file mode 100644 index 00000000..8b4df6a7 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/Doxyfile @@ -0,0 +1,18 @@ +PROJECT_NAME = "Facebook JNI" +PROJECT_BRIEF = "Helper library to provide safe and convenient access to JNI with very low overhead" +JAVADOC_AUTOBRIEF = YES +EXTRACT_ALL = YES +RECURSIVE = YES +EXCLUDE = tests Asserts.h Countable.h GlobalReference.h LocalReference.h LocalString.h Registration.h WeakReference.h jni_helpers.h Environment.h +EXCLUDE_PATTERNS = *-inl.h *.cpp +GENERATE_HTML = YES +GENERATE_LATEX = NO +ENABLE_PREPROCESSING = YES +HIDE_UNDOC_MEMBERS = YES +HIDE_SCOPE_NAMES = YES +HIDE_FRIEND_COMPOUNDS = YES +HIDE_UNDOC_CLASSES = YES +SHOW_INCLUDE_FILES = NO +PREDEFINED = LOG_TAG=fbjni +EXAMPLE_PATH = samples +#ENABLED_SECTIONS = INTERNAL diff --git a/lib/fb/src/main/cpp/include/fb/Environment.h b/lib/fb/src/main/cpp/include/fb/Environment.h new file mode 100644 index 00000000..e63db9cb --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/Environment.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include + +#include + +namespace facebook { +namespace jni { + +// Keeps a thread-local reference to the current thread's JNIEnv. +struct Environment { + // May be null if this thread isn't attached to the JVM + FBEXPORT static JNIEnv* current(); + static void initialize(JavaVM* vm); + + // There are subtle issues with calling the next functions directly. It is + // much better to always use a ThreadScope to manage attaching/detaching for + // you. + FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached(); + FBEXPORT static void detachCurrentThread(); +}; + +/** + * RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it + * exits will cause a crash, as will calling Detach an extra time, and this guard class helps + * keep that straight. In addition, it remembers whether it performed the attach or not, so it + * is safe to nest it with itself or with non-fbjni code that manages the attachment correctly. + * + * Potential concerns: + * - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the + * app is not busy. + * - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that + * there is no Java code on the current stack or you run the risk of a crash like: + * ERROR: detaching thread with interp frames (count=18) + * (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo) + * ThreadScope won't do a detach if the thread was already attached before the guard is + * instantiated, but there's probably some usage that could trip this up. + * - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language + * classes, not any of our application's classes. This will be different behavior than threads + * that were initiated on the Java side. A workaround is to pass a global reference for a + * class or instance to the new thread; this bypasses the need for the class loader. + * (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread) + * If you need access to the application's classes, you can use ThreadScope::WithClassLoader. + */ +class FBEXPORT ThreadScope { + public: + ThreadScope(); + ThreadScope(ThreadScope&) = delete; + ThreadScope(ThreadScope&&) = default; + ThreadScope& operator=(ThreadScope&) = delete; + ThreadScope& operator=(ThreadScope&&) = delete; + ~ThreadScope(); + + /** + * This runs the closure in a scope with fbjni's classloader. This should be + * the same classloader as the rest of the application and thus anything + * running in the closure will have access to the same classes as in a normal + * java-create thread. + */ + static void WithClassLoader(std::function&& runnable); + + static void OnLoad(); + private: + bool attachedWithThisScope_; +}; +} +} diff --git a/lib/fb/src/main/cpp/include/fb/ProgramLocation.h b/lib/fb/src/main/cpp/include/fb/ProgramLocation.h new file mode 100644 index 00000000..92b45406 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/ProgramLocation.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include + +namespace facebook { + +#define FROM_HERE facebook::ProgramLocation(__FUNCTION__, __FILE__, __LINE__) + +class ProgramLocation { +public: + ProgramLocation() : m_functionName("Unspecified"), m_fileName("Unspecified"), m_lineNumber(0) {} + + ProgramLocation(const char* functionName, const char* fileName, int line) : + m_functionName(functionName), + m_fileName(fileName), + m_lineNumber(line) + {} + + const char* functionName() const { return m_functionName; } + const char* fileName() const { return m_fileName; } + int lineNumber() const { return m_lineNumber; } + + std::string asFormattedString() const { + std::stringstream str; + str << "Function " << m_functionName << " in file " << m_fileName << ":" << m_lineNumber; + return str.str(); + } + + bool operator==(const ProgramLocation& other) const { + // Assumes that the strings are static + return (m_functionName == other.m_functionName) && (m_fileName == other.m_fileName) && m_lineNumber == other.m_lineNumber; + } + +private: + const char* m_functionName; + const char* m_fileName; + int m_lineNumber; +}; + +} diff --git a/lib/fb/src/main/cpp/include/fb/RefPtr.h b/lib/fb/src/main/cpp/include/fb/RefPtr.h new file mode 100644 index 00000000..ee19960f --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/RefPtr.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include + +namespace facebook { + +// Reference counting smart pointer. This is designed to work with the +// Countable class or other implementations in the future. It is designed in a +// way to be both efficient and difficult to misuse. Typical usage is very +// simple once you learn the patterns (and the compiler will help!): +// +// By default, the internal pointer is null. +// RefPtr ref; +// +// Object creation requires explicit construction: +// RefPtr ref = createNew(...); +// +// Or if the constructor is not public: +// RefPtr ref = adoptRef(new Foo(...)); +// +// But you can implicitly create from nullptr: +// RefPtr maybeRef = cond ? ref : nullptr; +// +// Move/Copy Construction/Assignment are straightforward: +// RefPtr ref2 = ref; +// ref = std::move(ref2); +// +// Destruction automatically drops the RefPtr's reference as expected. +// +// Upcasting is implicit but downcasting requires an explicit cast: +// struct Bar : public Foo {}; +// RefPtr barRef = static_cast>(ref); +// ref = barRef; +// +template +class RefPtr { +public: + constexpr RefPtr() : + m_ptr(nullptr) + {} + + // Allow implicit construction from a pointer only from nullptr + constexpr RefPtr(std::nullptr_t ptr) : + m_ptr(nullptr) + {} + + RefPtr(const RefPtr& ref) : + m_ptr(ref.m_ptr) + { + refIfNecessary(m_ptr); + } + + // Only allow implicit upcasts. A downcast will result in a compile error + // unless you use static_cast (which will end up invoking the explicit + // operator below). + template + RefPtr(const RefPtr& ref, typename std::enable_if::value, U>::type* = nullptr) : + m_ptr(ref.get()) + { + refIfNecessary(m_ptr); + } + + RefPtr(RefPtr&& ref) : + m_ptr(nullptr) + { + *this = std::move(ref); + } + + // Only allow implicit upcasts. A downcast will result in a compile error + // unless you use static_cast (which will end up invoking the explicit + // operator below). + template + RefPtr(RefPtr&& ref, typename std::enable_if::value, U>::type* = nullptr) : + m_ptr(nullptr) + { + *this = std::move(ref); + } + + ~RefPtr() { + unrefIfNecessary(m_ptr); + m_ptr = nullptr; + } + + RefPtr& operator=(const RefPtr& ref) { + if (m_ptr != ref.m_ptr) { + unrefIfNecessary(m_ptr); + m_ptr = ref.m_ptr; + refIfNecessary(m_ptr); + } + return *this; + } + + // The STL assumes rvalue references are unique and for simplicity's sake, we + // make the same assumption here, that &ref != this. + RefPtr& operator=(RefPtr&& ref) { + unrefIfNecessary(m_ptr); + m_ptr = ref.m_ptr; + ref.m_ptr = nullptr; + return *this; + } + + template + RefPtr& operator=(RefPtr&& ref) { + unrefIfNecessary(m_ptr); + m_ptr = ref.m_ptr; + ref.m_ptr = nullptr; + return *this; + } + + void reset() { + unrefIfNecessary(m_ptr); + m_ptr = nullptr; + } + + T* get() const { + return m_ptr; + } + + T* operator->() const { + return m_ptr; + } + + T& operator*() const { + return *m_ptr; + } + + template + explicit operator RefPtr () const; + + explicit operator bool() const { + return m_ptr ? true : false; + } + + bool isTheLastRef() const { + FBASSERT(m_ptr); + return m_ptr->hasOnlyOneRef(); + } + + // Creates a strong reference from a raw pointer, assuming that is already + // referenced from some other RefPtr. This should be used sparingly. + static inline RefPtr assumeAlreadyReffed(T* ptr) { + return RefPtr(ptr, ConstructionMode::External); + } + + // Creates a strong reference from a raw pointer, assuming that it points to a + // freshly-created object. See the documentation for RefPtr for usage. + static inline RefPtr adoptRef(T* ptr) { + return RefPtr(ptr, ConstructionMode::Adopted); + } + +private: + enum class ConstructionMode { + Adopted, + External + }; + + RefPtr(T* ptr, ConstructionMode mode) : + m_ptr(ptr) + { + FBASSERTMSGF(ptr, "Got null pointer in %s construction mode", mode == ConstructionMode::Adopted ? "adopted" : "external"); + ptr->ref(); + if (mode == ConstructionMode::Adopted) { + FBASSERT(ptr->hasOnlyOneRef()); + } + } + + static inline void refIfNecessary(T* ptr) { + if (ptr) { + ptr->ref(); + } + } + static inline void unrefIfNecessary(T* ptr) { + if (ptr) { + ptr->unref(); + } + } + + template friend class RefPtr; + + T* m_ptr; +}; + +// Creates a strong reference from a raw pointer, assuming that is already +// referenced from some other RefPtr and that it is non-null. This should be +// used sparingly. +template +static inline RefPtr assumeAlreadyReffed(T* ptr) { + return RefPtr::assumeAlreadyReffed(ptr); +} + +// As above, but tolerant of nullptr. +template +static inline RefPtr assumeAlreadyReffedOrNull(T* ptr) { + return ptr ? RefPtr::assumeAlreadyReffed(ptr) : nullptr; +} + +// Creates a strong reference from a raw pointer, assuming that it points to a +// freshly-created object. See the documentation for RefPtr for usage. +template +static inline RefPtr adoptRef(T* ptr) { + return RefPtr::adoptRef(ptr); +} + +template +static inline RefPtr createNew(Args&&... arguments) { + return RefPtr::adoptRef(new T(std::forward(arguments)...)); +} + +template template +RefPtr::operator RefPtr() const { + static_assert(std::is_base_of::value, "Invalid static cast"); + return assumeAlreadyReffedOrNull(static_cast(m_ptr)); +} + +template +inline bool operator==(const RefPtr& a, const RefPtr& b) { + return a.get() == b.get(); +} + +template +inline bool operator!=(const RefPtr& a, const RefPtr& b) { + return a.get() != b.get(); +} + +template +inline bool operator==(const RefPtr& ref, U* ptr) { + return ref.get() == ptr; +} + +template +inline bool operator!=(const RefPtr& ref, U* ptr) { + return ref.get() != ptr; +} + +template +inline bool operator==(U* ptr, const RefPtr& ref) { + return ref.get() == ptr; +} + +template +inline bool operator!=(U* ptr, const RefPtr& ref) { + return ref.get() != ptr; +} + +template +inline bool operator==(const RefPtr& ref, std::nullptr_t ptr) { + return ref.get() == ptr; +} + +template +inline bool operator!=(const RefPtr& ref, std::nullptr_t ptr) { + return ref.get() != ptr; +} + +template +inline bool operator==(std::nullptr_t ptr, const RefPtr& ref) { + return ref.get() == ptr; +} + +template +inline bool operator!=(std::nullptr_t ptr, const RefPtr& ref) { + return ref.get() != ptr; +} + +} diff --git a/lib/fb/src/main/cpp/include/fb/StaticInitialized.h b/lib/fb/src/main/cpp/include/fb/StaticInitialized.h new file mode 100644 index 00000000..c5ecc2fe --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/StaticInitialized.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include + +namespace facebook { + +// Class that lets you declare a global but does not add a static constructor +// to the binary. Eventually I'd like to have this auto-initialize in a +// multithreaded environment but for now it's easiest just to use manual +// initialization. +template +class StaticInitialized { +public: + constexpr StaticInitialized() : + m_instance(nullptr) + {} + + template + void initialize(Args&&... arguments) { + FBASSERT(!m_instance); + m_instance = new T(std::forward(arguments)...); + } + + T* operator->() const { + return m_instance; + } +private: + T* m_instance; +}; + +} diff --git a/lib/fb/src/main/cpp/include/fb/ThreadLocal.h b/lib/fb/src/main/cpp/include/fb/ThreadLocal.h new file mode 100644 index 00000000..1b868c44 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/ThreadLocal.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include + +namespace facebook { + +/////////////////////////////////////////////////////////////////////////////// + +/** + * A thread-local object is a "global" object within a thread. This is useful + * for writing apartment-threaded code, where nothing is actullay shared + * between different threads (hence no locking) but those variables are not + * on stack in local scope. To use it, just do something like this, + * + * ThreadLocal static_object; + * static_object->data_ = ...; + * static_object->doSomething(); + * + * ThreadLocal static_number; + * int value = *static_number; + * + * So, syntax-wise it's similar to pointers. T can be primitive types, and if + * it's a class, there has to be a default constructor. + */ +template +class ThreadLocal { +public: + /** + * Constructor that has to be called from a thread-neutral place. + */ + ThreadLocal() : + m_key(0), + m_cleanup(OnThreadExit) { + initialize(); + } + + /** + * As above but with a custom cleanup function + */ + typedef void (*CleanupFunction)(void* obj); + explicit ThreadLocal(CleanupFunction cleanup) : + m_key(0), + m_cleanup(cleanup) { + FBASSERT(cleanup); + initialize(); + } + + /** + * Access object's member or method through this operator overload. + */ + T *operator->() const { + return get(); + } + + T &operator*() const { + return *get(); + } + + T *get() const { + return (T*)pthread_getspecific(m_key); + } + + T* release() { + T* obj = get(); + pthread_setspecific(m_key, NULL); + return obj; + } + + void reset(T* other = NULL) { + T* old = (T*)pthread_getspecific(m_key); + if (old != other) { + FBASSERT(m_cleanup); + m_cleanup(old); + pthread_setspecific(m_key, other); + } + } + +private: + void initialize() { + int ret = pthread_key_create(&m_key, m_cleanup); + if (ret != 0) { + const char *msg = "(unknown error)"; + switch (ret) { + case EAGAIN: + msg = "PTHREAD_KEYS_MAX (1024) is exceeded"; + break; + case ENOMEM: + msg = "Out-of-memory"; + break; + } + (void) msg; + FBASSERTMSGF(0, "pthread_key_create failed: %d %s", ret, msg); + } + } + + static void OnThreadExit(void *obj) { + if (NULL != obj) { + delete (T*)obj; + } + } + + pthread_key_t m_key; + CleanupFunction m_cleanup; +}; + +} diff --git a/lib/fb/src/main/cpp/include/fb/assert.h b/lib/fb/src/main/cpp/include/fb/assert.h new file mode 100644 index 00000000..ea0c3f65 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/assert.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifndef FBASSERT_H +#define FBASSERT_H + +#include + +namespace facebook { +#define ENABLE_FBASSERT 1 + +#if ENABLE_FBASSERT +#define FBASSERTMSGF(expr, msg, ...) !(expr) ? facebook::assertInternal("Assert (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__) : (void) 0 +#else +#define FBASSERTMSGF(expr, msg, ...) +#endif // ENABLE_FBASSERT + +#define FBASSERT(expr) FBASSERTMSGF(expr, "%s", #expr) + +#define FBCRASH(msg, ...) facebook::assertInternal("Fatal error (%s:%d): " msg, __FILE__, __LINE__, ##__VA_ARGS__) +#define FBUNREACHABLE() facebook::assertInternal("This code should be unreachable (%s:%d)", __FILE__, __LINE__) + +FBEXPORT void assertInternal(const char* formatstr, ...) __attribute__((noreturn)); + +// This allows storing the assert message before the current process terminates due to a crash +typedef void (*AssertHandler)(const char* message); +void setAssertHandler(AssertHandler assertHandler); + +} // namespace facebook +#endif // FBASSERT_H diff --git a/lib/fb/src/main/cpp/include/fb/fbjni.h b/lib/fb/src/main/cpp/include/fb/fbjni.h new file mode 100644 index 00000000..e67aa164 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Boxed.h b/lib/fb/src/main/cpp/include/fb/fbjni/Boxed.h new file mode 100644 index 00000000..b4799616 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Boxed.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +namespace detail { +template +struct JPrimitive : JavaClass { + using typename JavaClass::javaobject; + using JavaClass::javaClassStatic; + static local_ref valueOf(jprim val) { + static auto cls = javaClassStatic(); + static auto method = + cls->template getStaticMethod("valueOf"); + return method(cls, val); + } + jprim value() const { + static auto method = + javaClassStatic()->template getMethod(T::kValueMethod); + return method(this->self()); + } +}; + +} // namespace detail + + +#define DEFINE_BOXED_PRIMITIVE(LITTLE, BIG) \ + struct J ## BIG : detail::JPrimitive { \ + static auto constexpr kJavaDescriptor = "Ljava/lang/" #BIG ";"; \ + static auto constexpr kValueMethod = #LITTLE "Value"; \ + j ## LITTLE LITTLE ## Value() const { \ + return value(); \ + } \ + }; \ + inline local_ref autobox(j ## LITTLE val) { \ + return J ## BIG::valueOf(val); \ + } + +DEFINE_BOXED_PRIMITIVE(boolean, Boolean) +DEFINE_BOXED_PRIMITIVE(byte, Byte) +DEFINE_BOXED_PRIMITIVE(char, Character) +DEFINE_BOXED_PRIMITIVE(short, Short) +DEFINE_BOXED_PRIMITIVE(int, Integer) +DEFINE_BOXED_PRIMITIVE(long, Long) +DEFINE_BOXED_PRIMITIVE(float, Float) +DEFINE_BOXED_PRIMITIVE(double, Double) + +#undef DEFINE_BOXED_PRIMITIVE + +inline local_ref autobox(alias_ref val) { + return make_local(val); +} + +}} + diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/ByteBuffer.h b/lib/fb/src/main/cpp/include/fb/fbjni/ByteBuffer.h new file mode 100644 index 00000000..21d17a27 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/ByteBuffer.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "CoreClasses.h" +#include "References-forward.h" + +namespace facebook { +namespace jni { + +// JNI's NIO support has some awkward preconditions and error reporting. This +// class provides much more user-friendly access. +class FBEXPORT JByteBuffer : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Ljava/nio/ByteBuffer;"; + + static local_ref wrapBytes(uint8_t* data, size_t size); + + bool isDirect() const; + + uint8_t* getDirectBytes() const; + size_t getDirectSize() const; +}; + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Common.h b/lib/fb/src/main/cpp/include/fb/fbjni/Common.h new file mode 100644 index 00000000..a7775db6 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Common.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** @file Common.h + * + * Defining the stuff that don't deserve headers of their own... + */ + +#pragma once + +#include + +#include + +#include +#include + +#ifdef FBJNI_DEBUG_REFS +# ifdef __ANDROID__ +# include +# else +# include +# endif +#endif + +// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as +// a C++ exception. +#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \ + ::facebook::jni::throwPendingJniExceptionAsCppException() + +// If the condition is true, throws a JniException object, which wraps the pending JNI Java +// exception if any. If no pending exception is found, throws a JniException object that wraps a +// RuntimeException throwable.  +#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \ + ::facebook::jni::throwCppExceptionIf(CONDITION) + +/// @cond INTERNAL + +namespace facebook { +namespace jni { + +FBEXPORT void throwPendingJniExceptionAsCppException(); +FBEXPORT void throwCppExceptionIf(bool condition); + +[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable); +[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg); +template +[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args); + + +/** + * This needs to be called at library load time, typically in your JNI_OnLoad method. + * + * The intended use is to return the result of initialize() directly + * from JNI_OnLoad and to do nothing else there. Library specific + * initialization code should go in the function passed to initialize + * (which can be, and probably should be, a C++ lambda). This approach + * provides correct error handling and translation errors during + * initialization into Java exceptions when appropriate. + * + * Failure to call this will cause your code to crash in a remarkably + * unhelpful way (typically a segfault) while trying to handle an exception + * which occurs later. + */ +FBEXPORT jint initialize(JavaVM*, std::function&&) noexcept; + +namespace internal { + +/** + * Retrieve a pointer the JNI environment of the current thread. + * + * @pre The current thread must be attached to the VM + */ +inline JNIEnv* getEnv() noexcept { + // TODO(T6594868) Benchmark against raw JNI access + return Environment::current(); +} + +// Define to get extremely verbose logging of references and to enable reference stats +#ifdef FBJNI_DEBUG_REFS +template +inline void dbglog(const char* msg, Args... args) { +# ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_VERBOSE, "fbjni_dbg", msg, args...); +# else + std::fprintf(stderr, msg, args...); +# endif +} + +#else + +template +inline void dbglog(const char*, Args...) { +} + +#endif + +}}} + +/// @endcond diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Context.h b/lib/fb/src/main/cpp/include/fb/fbjni/Context.h new file mode 100644 index 00000000..726a2533 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Context.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" +#include "File.h" + +namespace facebook { +namespace jni { + +class AContext : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Landroid/content/Context;"; + + // Define a method that calls into the represented Java class + local_ref getCacheDir() { + static auto method = getClass()->getMethod("getCacheDir"); + return method(self()); + } + + local_ref getFilesDir() { + static auto method = getClass()->getMethod("getFilesDir"); + return method(self()); + } +}; + +} +} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h new file mode 100644 index 00000000..1e00441a --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses-inl.h @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +#include "Common.h" +#include "Exceptions.h" +#include "Meta.h" +#include "MetaConvert.h" + +namespace facebook { +namespace jni { + +// jobject ///////////////////////////////////////////////////////////////////////////////////////// + +inline bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept { + return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE; +} + +inline local_ref JObject::getClass() const noexcept { + return adopt_local(internal::getEnv()->GetObjectClass(self())); +} + +inline bool JObject::isInstanceOf(alias_ref cls) const noexcept { + return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE; +} + +template +inline T JObject::getFieldValue(JField field) const noexcept { + return field.get(self()); +} + +template +inline local_ref JObject::getFieldValue(JField field) const noexcept { + return adopt_local(field.get(self())); +} + +template +inline void JObject::setFieldValue(JField field, T value) noexcept { + field.set(self(), value); +} + +inline std::string JObject::toString() const { + static auto method = findClassLocal("java/lang/Object")->getMethod("toString"); + + return method(self())->toStdString(); +} + + +// Class is here instead of CoreClasses.h because we need +// alias_ref to be complete. +class MonitorLock { + public: + inline MonitorLock() noexcept; + inline MonitorLock(alias_ref object) noexcept; + inline ~MonitorLock() noexcept; + + inline MonitorLock(MonitorLock&& other) noexcept; + inline MonitorLock& operator=(MonitorLock&& other) noexcept; + + inline MonitorLock(const MonitorLock&) = delete; + inline MonitorLock& operator=(const MonitorLock&) = delete; + + private: + inline void reset() noexcept; + alias_ref owned_; +}; + +MonitorLock::MonitorLock() noexcept : owned_(nullptr) {} + +MonitorLock::MonitorLock(alias_ref object) noexcept + : owned_(object) { + internal::getEnv()->MonitorEnter(object.get()); +} + +void MonitorLock::reset() noexcept { + if (owned_) { + internal::getEnv()->MonitorExit(owned_.get()); + if (internal::getEnv()->ExceptionCheck()) { + abort(); // Lock mismatch + } + owned_ = nullptr; + } +} + +MonitorLock::~MonitorLock() noexcept { + reset(); +} + +MonitorLock::MonitorLock(MonitorLock&& other) noexcept + : owned_(other.owned_) +{ + other.owned_ = nullptr; +} + +MonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept { + reset(); + owned_ = other.owned_; + other.owned_ = nullptr; + return *this; +} + +inline MonitorLock JObject::lock() const noexcept { + return MonitorLock(this_); +} + +inline jobject JObject::self() const noexcept { + return this_; +} + +inline void swap(JObject& a, JObject& b) noexcept { + using std::swap; + swap(a.this_, b.this_); +} + +// JavaClass /////////////////////////////////////////////////////////////////////////////////////// + +namespace detail { +template +static local_ref newInstance(Args... args) { + static auto cls = JC::javaClassStatic(); + static auto constructor = cls->template getConstructor(); + return cls->newObject(constructor, args...); +} +} + + +template +auto JavaClass::self() const noexcept -> javaobject { + return static_cast(JObject::self()); +} + +// jclass ////////////////////////////////////////////////////////////////////////////////////////// + +namespace detail { + +// This is not a real type. It is used so people won't accidentally +// use a void* to initialize a NativeMethod. +struct NativeMethodWrapper; + +} + +struct NativeMethod { + const char* name; + std::string descriptor; + detail::NativeMethodWrapper* wrapper; +}; + +inline local_ref JClass::getSuperclass() const noexcept { + return adopt_local(internal::getEnv()->GetSuperclass(self())); +} + +inline void JClass::registerNatives(std::initializer_list methods) { + const auto env = internal::getEnv(); + + JNINativeMethod jnimethods[methods.size()]; + size_t i = 0; + for (auto it = methods.begin(); it < methods.end(); ++it, ++i) { + jnimethods[i].name = it->name; + jnimethods[i].signature = it->descriptor.c_str(); + jnimethods[i].fnPtr = reinterpret_cast(it->wrapper); + } + + auto result = env->RegisterNatives(self(), jnimethods, methods.size()); + FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK); +} + +inline bool JClass::isAssignableFrom(alias_ref other) const noexcept { + const auto env = internal::getEnv(); + const auto result = env->IsAssignableFrom(self(), other.get()); + return result; +} + +template +inline JConstructor JClass::getConstructor() const { + return getConstructor(jmethod_traits_from_cxx::constructor_descriptor().c_str()); +} + +template +inline JConstructor JClass::getConstructor(const char* descriptor) const { + constexpr auto constructor_method_name = ""; + return getMethod(constructor_method_name, descriptor); +} + +template +inline JMethod JClass::getMethod(const char* name) const { + return getMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); +} + +template +inline JMethod JClass::getMethod( + const char* name, + const char* descriptor) const { + const auto env = internal::getEnv(); + const auto method = env->GetMethodID(self(), name, descriptor); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); + return JMethod{method}; +} + +template +inline JStaticMethod JClass::getStaticMethod(const char* name) const { + return getStaticMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); +} + +template +inline JStaticMethod JClass::getStaticMethod( + const char* name, + const char* descriptor) const { + const auto env = internal::getEnv(); + const auto method = env->GetStaticMethodID(self(), name, descriptor); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); + return JStaticMethod{method}; +} + +template +inline JNonvirtualMethod JClass::getNonvirtualMethod(const char* name) const { + return getNonvirtualMethod(name, jmethod_traits_from_cxx::descriptor().c_str()); +} + +template +inline JNonvirtualMethod JClass::getNonvirtualMethod( + const char* name, + const char* descriptor) const { + const auto env = internal::getEnv(); + const auto method = env->GetMethodID(self(), name, descriptor); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!method); + return JNonvirtualMethod{method}; +} + +template +inline JField(), T>> +JClass::getField(const char* name) const { + return getField(name, jtype_traits::descriptor().c_str()); +} + +template +inline JField(), T>> JClass::getField( + const char* name, + const char* descriptor) const { + const auto env = internal::getEnv(); + auto field = env->GetFieldID(self(), name, descriptor); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!field); + return JField{field}; +} + +template +inline JStaticField(), T>> JClass::getStaticField( + const char* name) const { + return getStaticField(name, jtype_traits::descriptor().c_str()); +} + +template +inline JStaticField(), T>> JClass::getStaticField( + const char* name, + const char* descriptor) const { + const auto env = internal::getEnv(); + auto field = env->GetStaticFieldID(self(), name, descriptor); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!field); + return JStaticField{field}; +} + +template +inline T JClass::getStaticFieldValue(JStaticField field) const noexcept { + return field.get(self()); +} + +template +inline local_ref JClass::getStaticFieldValue(JStaticField field) noexcept { + return adopt_local(field.get(self())); +} + +template +inline void JClass::setStaticFieldValue(JStaticField field, T value) noexcept { + field.set(self(), value); +} + +template +inline local_ref JClass::newObject( + JConstructor constructor, + Args... args) const { + const auto env = internal::getEnv(); + auto object = env->NewObject(self(), constructor.getId(), + detail::callToJni( + detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!object); + return adopt_local(static_cast(object)); +} + +inline jclass JClass::self() const noexcept { + return static_cast(JObject::self()); +} + +inline void registerNatives(const char* name, std::initializer_list methods) { + findClassLocal(name)->registerNatives(methods); +} + + +// jstring ///////////////////////////////////////////////////////////////////////////////////////// + +inline local_ref make_jstring(const std::string& modifiedUtf8) { + return make_jstring(modifiedUtf8.c_str()); +} + +namespace detail { +// convert to std::string from jstring +template <> +struct Convert { + typedef jstring jniType; + static std::string fromJni(jniType t) { + return wrap_alias(t)->toStdString(); + } + static jniType toJniRet(const std::string& t) { + return make_jstring(t).release(); + } + static local_ref toCall(const std::string& t) { + return make_jstring(t); + } +}; + +// convert return from const char* +template <> +struct Convert { + typedef jstring jniType; + // no automatic synthesis of const char*. (It can't be freed.) + static jniType toJniRet(const char* t) { + return make_jstring(t).release(); + } + static local_ref toCall(const char* t) { + return make_jstring(t); + } +}; +} + +// jthrowable ////////////////////////////////////////////////////////////////////////////////////// + +inline local_ref JThrowable::initCause(alias_ref cause) { + static auto meth = javaClassStatic()->getMethod("initCause"); + return meth(self(), cause.get()); +} + +// jtypeArray ////////////////////////////////////////////////////////////////////////////////////// + +namespace detail { +inline size_t JArray::size() const noexcept { + const auto env = internal::getEnv(); + return env->GetArrayLength(self()); +} +} + +namespace detail { +template +inline ElementProxy::ElementProxy( + Target* target, + size_t idx) + : target_{target}, idx_{idx} {} + +template +inline ElementProxy& ElementProxy::operator=(const T& o) { + target_->setElement(idx_, o); + return *this; +} + +template +inline ElementProxy& ElementProxy::operator=(alias_ref& o) { + target_->setElement(idx_, o.get()); + return *this; +} + +template +inline ElementProxy& ElementProxy::operator=(alias_ref&& o) { + target_->setElement(idx_, o.get()); + return *this; +} + +template +inline ElementProxy& ElementProxy::operator=(const ElementProxy& o) { + auto src = o.target_->getElement(o.idx_); + target_->setElement(idx_, src.get()); + return *this; +} + +template +inline ElementProxy::ElementProxy::operator const local_ref () const { + return target_->getElement(idx_); +} + +template +inline ElementProxy::ElementProxy::operator local_ref () { + return target_->getElement(idx_); +} +} + +template +std::string JArrayClass::get_instantiated_java_descriptor() { + return "[" + jtype_traits::descriptor(); +}; + +template +std::string JArrayClass::get_instantiated_base_name() { + return get_instantiated_java_descriptor(); +}; + +template +auto JArrayClass::newArray(size_t size) -> local_ref { + static auto elementClass = findClassStatic(jtype_traits::base_name().c_str()); + const auto env = internal::getEnv(); + auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray); + return adopt_local(static_cast(rawArray)); +} + +template +inline void JArrayClass::setElement(size_t idx, const T& value) { + const auto env = internal::getEnv(); + env->SetObjectArrayElement(this->self(), idx, value); +} + +template +inline local_ref JArrayClass::getElement(size_t idx) { + const auto env = internal::getEnv(); + auto rawElement = env->GetObjectArrayElement(this->self(), idx); + return adopt_local(static_cast(rawElement)); +} + +template +inline detail::ElementProxy> JArrayClass::operator[](size_t index) { + return detail::ElementProxy>(this, index); +} + +// jarray ///////////////////////////////////////////////////////////////////////////////////////// + +template +auto JPrimitiveArray::getRegion(jsize start, jsize length) + -> std::unique_ptr { + using T = typename jtype_traits::entry_type; + auto buf = std::unique_ptr{new T[length]}; + getRegion(start, length, buf.get()); + return buf; +} + +template +std::string JPrimitiveArray::get_instantiated_java_descriptor() { + return jtype_traits::descriptor(); +} +template +std::string JPrimitiveArray::get_instantiated_base_name() { + return JPrimitiveArray::get_instantiated_java_descriptor(); +} + +template +auto JPrimitiveArray::pin() -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), 0, 0}; +} + +template +auto JPrimitiveArray::pinRegion(jsize start, jsize length) + -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), start, length}; +} + +template +auto JPrimitiveArray::pinCritical() + -> PinnedPrimitiveArray> { + return PinnedPrimitiveArray>{this->self(), 0, 0}; +} + +template +class PinnedArrayAlloc { + public: + static void allocate( + alias_ref::array_type> array, + jsize start, + jsize length, + T** elements, + size_t* size, + jboolean* isCopy) { + (void) start; + (void) length; + *elements = array->getElements(isCopy); + *size = array->size(); + } + static void release( + alias_ref::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + (void) start; + (void) size; + array->releaseElements(elements, mode); + } +}; + +template +class PinnedCriticalAlloc { + public: + static void allocate( + alias_ref::array_type> array, + jsize start, + jsize length, + T** elements, + size_t* size, + jboolean* isCopy) { + const auto env = internal::getEnv(); + *elements = static_cast(env->GetPrimitiveArrayCritical(array.get(), isCopy)); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements); + *size = array->size(); + } + static void release( + alias_ref::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + const auto env = internal::getEnv(); + env->ReleasePrimitiveArrayCritical(array.get(), elements, mode); + } +}; + +template +class PinnedRegionAlloc { + public: + static void allocate( + alias_ref::array_type> array, + jsize start, + jsize length, + T** elements, + size_t* size, + jboolean* isCopy) { + auto buf = array->getRegion(start, length); + FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf); + *elements = buf.release(); + *size = length; + *isCopy = true; + } + static void release( + alias_ref::array_type> array, + T* elements, + jint start, + jint size, + jint mode) { + std::unique_ptr holder; + if (mode == 0 || mode == JNI_ABORT) { + holder.reset(elements); + } + if (mode == 0 || mode == JNI_COMMIT) { + array->setRegion(start, size, elements); + } + } +}; + +// PinnedPrimitiveArray /////////////////////////////////////////////////////////////////////////// + +template +PinnedPrimitiveArray::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) { + *this = std::move(o); +} + +template +PinnedPrimitiveArray& +PinnedPrimitiveArray::operator=(PinnedPrimitiveArray&& o) { + if (array_) { + release(); + } + array_ = std::move(o.array_); + elements_ = o.elements_; + isCopy_ = o.isCopy_; + size_ = o.size_; + start_ = o.start_; + o.clear(); + return *this; +} + +template +T* PinnedPrimitiveArray::get() { + return elements_; +} + +template +inline void PinnedPrimitiveArray::release() { + releaseImpl(0); + clear(); +} + +template +inline void PinnedPrimitiveArray::commit() { + releaseImpl(JNI_COMMIT); +} + +template +inline void PinnedPrimitiveArray::abort() { + releaseImpl(JNI_ABORT); + clear(); +} + +template +inline void PinnedPrimitiveArray::releaseImpl(jint mode) { + FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr); + Alloc::release(array_, elements_, start_, size_, mode); +} + +template +inline void PinnedPrimitiveArray::clear() noexcept { + array_ = nullptr; + elements_ = nullptr; + isCopy_ = false; + start_ = 0; + size_ = 0; +} + +template +inline T& PinnedPrimitiveArray::operator[](size_t index) { + FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr); + return elements_[index]; +} + +template +inline bool PinnedPrimitiveArray::isCopy() const noexcept { + return isCopy_ == JNI_TRUE; +} + +template +inline size_t PinnedPrimitiveArray::size() const noexcept { + return size_; +} + +template +inline PinnedPrimitiveArray::~PinnedPrimitiveArray() noexcept { + if (elements_) { + release(); + } +} + +template +inline PinnedPrimitiveArray::PinnedPrimitiveArray(alias_ref::array_type> array, jint start, jint length) { + array_ = array; + start_ = start; + Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_); +} + +template +inline alias_ref JavaClass::javaClassStatic() { + static auto cls = findClassStatic(jtype_traits::base_name().c_str()); + return cls; +} + +template +inline local_ref JavaClass::javaClassLocal() { + std::string className(jtype_traits::base_name().c_str()); + return findClassLocal(className.c_str()); +} + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h b/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h new file mode 100644 index 00000000..b4d76a07 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/CoreClasses.h @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +/** @file CoreClasses.h + * + * In CoreClasses.h wrappers for the core classes (jobject, jclass, and jstring) is defined + * to provide access to corresponding JNI functions + some conveniance. + */ + +#include "References-forward.h" +#include "Meta-forward.h" +#include "TypeTraits.h" + +#include + +#include + +#include + +namespace facebook { +namespace jni { + +class JClass; +class JObject; + +/// Lookup a class by name. Note this functions returns an alias_ref that +/// points to a leaked global reference. This is appropriate for classes +/// that are never unloaded (which is any class in an Android app and most +/// Java programs). +/// +/// The most common use case for this is storing the result +/// in a "static auto" variable, or a static global. +/// +/// @return Returns a leaked global reference to the class +FBEXPORT alias_ref findClassStatic(const char* name); + +/// Lookup a class by name. Note this functions returns a local reference, +/// which means that it must not be stored in a static variable. +/// +/// The most common use case for this is one-time initialization +/// (like caching method ids). +/// +/// @return Returns a global reference to the class +FBEXPORT local_ref findClassLocal(const char* name); + +/// Check to see if two references refer to the same object. Comparison with nullptr +/// returns true if and only if compared to another nullptr. A weak reference that +/// refers to a reclaimed object count as nullptr. +FBEXPORT bool isSameObject(alias_ref lhs, alias_ref rhs) noexcept; + +// Together, these classes allow convenient use of any class with the fbjni +// helpers. To use: +// +// struct MyClass : public JavaClass { +// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; +// }; +// +// Then, an alias_ref will be backed by an instance of +// MyClass. JavaClass provides a convenient way to add functionality to these +// smart references. +// +// For example: +// +// struct MyClass : public JavaClass { +// constexpr static auto kJavaDescriptor = "Lcom/example/package/MyClass;"; +// +// void foo() { +// static auto method = javaClassStatic()->getMethod("foo"); +// method(self()); +// } +// +// static local_ref create(int i) { +// return newInstance(i); +// } +// }; +// +// auto obj = MyClass::create(10); +// obj->foo(); +// +// While users of a JavaClass-type can lookup methods and fields through the +// underlying JClass, those calls can only be checked at runtime. It is recommended +// that the JavaClass-type instead explicitly expose it's methods as in the example +// above. + +namespace detail { +template +static local_ref newInstance(Args... args); +} + +class MonitorLock; + +class FBEXPORT JObject : detail::JObjectBase { +public: + static constexpr auto kJavaDescriptor = "Ljava/lang/Object;"; + + static constexpr const char* get_instantiated_java_descriptor() { return nullptr; } + static constexpr const char* get_instantiated_base_name() { return nullptr; } + + /// Get a @ref local_ref of the object's class + local_ref getClass() const noexcept; + + /// Checks if the object is an instance of a class + bool isInstanceOf(alias_ref cls) const noexcept; + + /// Get the primitive value of a field + template + T getFieldValue(JField field) const noexcept; + + /// Get and wrap the value of a field in a @ref local_ref + template + local_ref getFieldValue(JField field) const noexcept; + + /// Set the value of field. Any Java type is accepted, including the primitive types + /// and raw reference types. + template + void setFieldValue(JField field, T value) noexcept; + + /// Convenience method to create a std::string representing the object + std::string toString() const; + + // Take this object's monitor lock + MonitorLock lock() const noexcept; + + typedef _jobject _javaobject; + typedef _javaobject* javaobject; + +protected: + jobject self() const noexcept; +private: + friend void swap(JObject& a, JObject& b) noexcept; + template + friend struct detail::ReprAccess; + template + friend class JavaClass; + + template + friend class JObjectWrapper; +}; + +// This is only to maintain backwards compatibility with things that are +// already providing a specialization of JObjectWrapper. Any such instances +// should be updated to use a JavaClass. +template<> +class JObjectWrapper : public JObject { +}; + + +namespace detail { +template +struct JTypeFor { + static_assert( + std::is_base_of< + std::remove_pointer::type, + typename std::remove_pointer::type + >::value, ""); + using _javaobject = typename std::remove_pointer::type; + using javaobject = JType; +}; + +template +struct JTypeFor { + // JNI pattern for jobject assignable pointer + struct _javaobject : Base::_javaobject { + // This allows us to map back to the defining type (in ReprType, for + // example). + typedef T JniRefRepr; + }; + using javaobject = _javaobject*; +}; +} + +// JavaClass provides a method to inform fbjni about user-defined Java types. +// Given a class: +// struct Foo : JavaClass { +// static constexpr auto kJavaDescriptor = "Lcom/example/package/Foo;"; +// }; +// fbjni can determine the java type/method signatures for Foo::javaobject and +// smart refs (like alias_ref) will hold an instance of Foo +// and provide access to it through the -> and * operators. +// +// The "Base" template argument can be used to specify the JavaClass superclass +// of this type (for instance, JString's Base is JObject). +// +// The "JType" template argument is used to provide a jni type (like jstring, +// jthrowable) to be used as javaobject. This should only be necessary for +// built-in jni types and not user-defined ones. +template +class FBEXPORT JavaClass : public Base { + using JObjType = typename detail::JTypeFor; +public: + using _javaobject = typename JObjType::_javaobject; + using javaobject = typename JObjType::javaobject; + + using JavaBase = JavaClass; + + static alias_ref javaClassStatic(); + static local_ref javaClassLocal(); +protected: + /// Allocates a new object and invokes the specified constructor + /// Like JClass's getConstructor, this function can only check at runtime if + /// the class actually has a constructor that accepts the corresponding types. + /// While a JavaClass-type can expose this function directly, it is recommended + /// to instead to use this to explicitly only expose those constructors that + /// the Java class actually has (i.e. with static create() functions). + template + static local_ref newInstance(Args... args) { + return detail::newInstance(args...); + } + + javaobject self() const noexcept; +}; + +/// Wrapper to provide functionality to jclass references +struct NativeMethod; + +class FBEXPORT JClass : public JavaClass { + public: + /// Java type descriptor + static constexpr const char* kJavaDescriptor = "Ljava/lang/Class;"; + + /// Get a @local_ref to the super class of this class + local_ref getSuperclass() const noexcept; + + /// Register native methods for the class. Usage looks like this: + /// + /// classRef->registerNatives({ + /// makeNativeMethod("nativeMethodWithAutomaticDescriptor", + /// methodWithAutomaticDescriptor), + /// makeNativeMethod("nativeMethodWithExplicitDescriptor", + /// "(Lcom/facebook/example/MyClass;)V", + /// methodWithExplicitDescriptor), + /// }); + /// + /// By default, C++ exceptions raised will be converted to Java exceptions. + /// To avoid this and get the "standard" JNI behavior of a crash when a C++ + /// exception is crashing out of the JNI method, declare the method noexcept. + void registerNatives(std::initializer_list methods); + + /// Check to see if the class is assignable from another class + /// @pre cls != nullptr + bool isAssignableFrom(alias_ref cls) const noexcept; + + /// Convenience method to lookup the constructor with descriptor as specified by the + /// type arguments + template + JConstructor getConstructor() const; + + /// Convenience method to lookup the constructor with specified descriptor + template + JConstructor getConstructor(const char* descriptor) const; + + /// Look up the method with given name and descriptor as specified with the type arguments + template + JMethod getMethod(const char* name) const; + + /// Look up the method with given name and descriptor + template + JMethod getMethod(const char* name, const char* descriptor) const; + + /// Lookup the field with the given name and deduced descriptor + template + JField(), T>> getField(const char* name) const; + + /// Lookup the field with the given name and descriptor + template + JField(), T>> getField(const char* name, const char* descriptor) const; + + /// Lookup the static field with the given name and deduced descriptor + template + JStaticField(), T>> getStaticField(const char* name) const; + + /// Lookup the static field with the given name and descriptor + template + JStaticField(), T>> getStaticField( + const char* name, + const char* descriptor) const; + + /// Get the primitive value of a static field + template + T getStaticFieldValue(JStaticField field) const noexcept; + + /// Get and wrap the value of a field in a @ref local_ref + template + local_ref getStaticFieldValue(JStaticField field) noexcept; + + /// Set the value of field. Any Java type is accepted, including the primitive types + /// and raw reference types. + template + void setStaticFieldValue(JStaticField field, T value) noexcept; + + /// Allocates a new object and invokes the specified constructor + template + local_ref newObject(JConstructor constructor, Args... args) const; + + /// Look up the static method with given name and descriptor as specified with the type arguments + template + JStaticMethod getStaticMethod(const char* name) const; + + /// Look up the static method with given name and descriptor + template + JStaticMethod getStaticMethod(const char* name, const char* descriptor) const; + + /// Look up the non virtual method with given name and descriptor as specified with the + /// type arguments + template + JNonvirtualMethod getNonvirtualMethod(const char* name) const; + + /// Look up the non virtual method with given name and descriptor + template + JNonvirtualMethod getNonvirtualMethod(const char* name, const char* descriptor) const; + +private: + jclass self() const noexcept; +}; + +// Convenience method to register methods on a class without holding +// onto the class object. +void registerNatives(const char* name, std::initializer_list methods); + +/// Wrapper to provide functionality to jstring references +class FBEXPORT JString : public JavaClass { + public: + /// Java type descriptor + static constexpr const char* kJavaDescriptor = "Ljava/lang/String;"; + + /// Convenience method to convert a jstring object to a std::string + std::string toStdString() const; +}; + +/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a +/// jstring +FBEXPORT local_ref make_jstring(const char* modifiedUtf8); +FBEXPORT local_ref make_jstring(const std::string& modifiedUtf8); + +/// Wrapper to provide functionality to jthrowable references +class FBEXPORT JThrowable : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Ljava/lang/Throwable;"; + + local_ref initCause(alias_ref cause); +}; + +namespace detail { +template +class ElementProxy { + private: + Target* target_; + size_t idx_; + + public: + using T = typename Target::javaentry; + ElementProxy(Target* target, size_t idx); + + ElementProxy& operator=(const T& o); + + ElementProxy& operator=(alias_ref& o); + + ElementProxy& operator=(alias_ref&& o); + + ElementProxy& operator=(const ElementProxy& o); + + operator const local_ref () const; + + operator local_ref (); +}; +} + +namespace detail { +class FBEXPORT JArray : public JavaClass { + public: + // This cannot be used in a scope that derives a descriptor (like in a method + // signature). Use a more derived type instead (like JArrayInt or + // JArrayClass). + static constexpr const char* kJavaDescriptor = nullptr; + size_t size() const noexcept; +}; + +// This is used so that the JArrayClass javaobject extends jni's +// jobjectArray. This class should not be used directly. A general Object[] +// should use JArrayClass. +class FBEXPORT JTypeArray : public JavaClass { + // This cannot be used in a scope that derives a descriptor (like in a method + // signature). + static constexpr const char* kJavaDescriptor = nullptr; +}; +} + +template +class JArrayClass : public JavaClass, detail::JTypeArray> { + public: + static_assert(is_plain_jni_reference(), ""); + // javaentry is the jni type of an entry in the array (i.e. jint). + using javaentry = T; + // javaobject is the jni type of the array. + using javaobject = typename JavaClass, detail::JTypeArray>::javaobject; + static constexpr const char* kJavaDescriptor = nullptr; + static std::string get_instantiated_java_descriptor(); + static std::string get_instantiated_base_name(); + + /// Allocate a new array from Java heap, for passing as a JNI parameter or return value. + /// NOTE: if using as a return value, you want to call release() instead of get() on the + /// smart pointer. + static local_ref newArray(size_t count); + + /// Assign an object to the array. + /// Typically you will use the shorthand (*ref)[idx]=value; + void setElement(size_t idx, const T& value); + + /// Read an object from the array. + /// Typically you will use the shorthand + /// T value = (*ref)[idx]; + /// If you use auto, you'll get an ElementProxy, which may need to be cast. + local_ref getElement(size_t idx); + + /// EXPERIMENTAL SUBSCRIPT SUPPORT + /// This implementation of [] returns a proxy object which then has a bunch of specializations + /// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can + /// make code look like it is dealing with a T rather than an obvious proxy. In particular, the + /// proxy in this iteration does not read a value and therefore does not create a LocalRef + /// until one of these other operators is used. There are certainly holes that you may find + /// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand, + /// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests + /// for some examples. + detail::ElementProxy operator[](size_t idx); +}; + +template +using jtypeArray = typename JArrayClass::javaobject; + +template +local_ref::javaobject> adopt_local_array(jobjectArray ref) { + return adopt_local(static_cast::javaobject>(ref)); +} + +template +local_ref adopt_local(detail::ElementProxy elementProxy) { + return static_cast>(elementProxy); +} + +template +class PinnedPrimitiveArray; + +template class PinnedArrayAlloc; +template class PinnedRegionAlloc; +template class PinnedCriticalAlloc; + +/// Wrapper to provide functionality to jarray references. +/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with +/// the elements of the array. +template +class FBEXPORT JPrimitiveArray : + public JavaClass, detail::JArray, JArrayType> { + static_assert(is_jni_primitive_array(), ""); + public: + static constexpr const char* kJavaDescriptor = nullptr; + static std::string get_instantiated_java_descriptor(); + static std::string get_instantiated_base_name(); + + using T = typename jtype_traits::entry_type; + + static local_ref newArray(size_t count); + + void getRegion(jsize start, jsize length, T* buf); + std::unique_ptr getRegion(jsize start, jsize length); + void setRegion(jsize start, jsize length, const T* buf); + + /// Returns a view of the underlying array. This will either be a "pinned" + /// version of the array (in which case changes to one immediately affect the + /// other) or a copy of the array (in which cases changes to the view will take + /// affect when destroyed or on calls to release()/commit()). + PinnedPrimitiveArray> pin(); + + /// Returns a view of part of the underlying array. A pinned region is always + /// backed by a copy of the region. + PinnedPrimitiveArray> pinRegion(jsize start, jsize length); + + /// Returns a view of the underlying array like pin(). However, while the pin + /// is held, the code is considered within a "critical region". In a critical + /// region, native code must not call JNI functions or make any calls that may + /// block on other Java threads. These restrictions make it more likely that + /// the view will be "pinned" rather than copied (for example, the VM may + /// suspend garbage collection within a critical region). + PinnedPrimitiveArray> pinCritical(); + +private: + friend class PinnedArrayAlloc; + T* getElements(jboolean* isCopy); + void releaseElements(T* elements, jint mode); +}; + +FBEXPORT local_ref make_boolean_array(jsize size); +FBEXPORT local_ref make_byte_array(jsize size); +FBEXPORT local_ref make_char_array(jsize size); +FBEXPORT local_ref make_short_array(jsize size); +FBEXPORT local_ref make_int_array(jsize size); +FBEXPORT local_ref make_long_array(jsize size); +FBEXPORT local_ref make_float_array(jsize size); +FBEXPORT local_ref make_double_array(jsize size); + +using JArrayBoolean = JPrimitiveArray; +using JArrayByte = JPrimitiveArray; +using JArrayChar = JPrimitiveArray; +using JArrayShort = JPrimitiveArray; +using JArrayInt = JPrimitiveArray; +using JArrayLong = JPrimitiveArray; +using JArrayFloat = JPrimitiveArray; +using JArrayDouble = JPrimitiveArray; + +/// RAII class for pinned primitive arrays +/// This currently only supports read/write access to existing java arrays. You can't create a +/// primitive array this way yet. This class also pins the entire array into memory during the +/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the +/// release() or abort() functions. During a long-running block of code, you +/// should unpin the array as soon as you're done with it, to avoid holding up +/// the Java garbage collector. +template +class PinnedPrimitiveArray { + public: + static_assert(is_jni_primitive::value, + "PinnedPrimitiveArray requires primitive jni type."); + + using ArrayType = typename jtype_traits::array_type; + + PinnedPrimitiveArray(PinnedPrimitiveArray&&); + PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete; + ~PinnedPrimitiveArray() noexcept; + + PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&); + PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete; + + T* get(); + void release(); + /// Unpins the array. If the array is a copy, pending changes are discarded. + void abort(); + /// If the array is a copy, copies pending changes to the underlying java array. + void commit(); + + bool isCopy() const noexcept; + + const T& operator[](size_t index) const; + T& operator[](size_t index); + size_t size() const noexcept; + + private: + alias_ref array_; + size_t start_; + T* elements_; + jboolean isCopy_; + size_t size_; + + void allocate(alias_ref, jint start, jint length); + void releaseImpl(jint mode); + void clear() noexcept; + + PinnedPrimitiveArray(alias_ref, jint start, jint length); + + friend class JPrimitiveArray::array_type>; +}; + +#pragma push_macro("PlainJniRefMap") +#undef PlainJniRefMap +#define PlainJniRefMap(rtype, jtype) \ +namespace detail { \ +template<> \ +struct RefReprType { \ + using type = rtype; \ +}; \ +} + +PlainJniRefMap(JArrayBoolean, jbooleanArray); +PlainJniRefMap(JArrayByte, jbyteArray); +PlainJniRefMap(JArrayChar, jcharArray); +PlainJniRefMap(JArrayShort, jshortArray); +PlainJniRefMap(JArrayInt, jintArray); +PlainJniRefMap(JArrayLong, jlongArray); +PlainJniRefMap(JArrayFloat, jfloatArray); +PlainJniRefMap(JArrayDouble, jdoubleArray); +PlainJniRefMap(JObject, jobject); +PlainJniRefMap(JClass, jclass); +PlainJniRefMap(JString, jstring); +PlainJniRefMap(JThrowable, jthrowable); + +#pragma pop_macro("PlainJniRefMap") + +}} + +#include "CoreClasses-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Exceptions.h b/lib/fb/src/main/cpp/include/fb/fbjni/Exceptions.h new file mode 100644 index 00000000..08deb42e --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Exceptions.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * @file Exceptions.h + * + * After invoking a JNI function that can throw a Java exception, the macro + * @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref FACEBOOK_JNI_THROW_EXCEPTION_IF() + * should be invoked. + * + * IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! + * To use these methods you MUST call initExceptionHelpers() when your library is loaded. + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +#include "Common.h" +#include "References.h" +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +class JThrowable; + +class JCppException : public JavaClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/CppException;"; + + static local_ref create(const char* str) { + return newInstance(make_jstring(str)); + } + + static local_ref create(const std::exception& ex) { + return newInstance(make_jstring(ex.what())); + } +}; + +// JniException //////////////////////////////////////////////////////////////////////////////////// + +/** + * This class wraps a Java exception into a C++ exception; if the exception is routed back + * to the Java side, it can be unwrapped and just look like a pure Java interaction. The class + * is resilient to errors while creating the exception, falling back to some pre-allocated + * exceptions if a new one cannot be allocated or populated. + * + * Note: the what() method of this class is not thread-safe (t6900503). + */ +class FBEXPORT JniException : public std::exception { + public: + JniException(); + ~JniException(); + + explicit JniException(alias_ref throwable); + + JniException(JniException &&rhs); + + JniException(const JniException &other); + + local_ref getThrowable() const noexcept; + + virtual const char* what() const noexcept; + + void setJavaException() const noexcept; + + private: + global_ref throwable_; + mutable std::string what_; + mutable bool isMessageExtracted_; + const static std::string kExceptionMessageFailure_; + + void populateWhat() const noexcept; +}; + +// Exception throwing & translating functions ////////////////////////////////////////////////////// + +// Functions that throw C++ exceptions + +static const int kMaxExceptionMessageBufferSize = 512; + +// These methods are the preferred way to throw a Java exception from +// a C++ function. They create and throw a C++ exception which wraps +// a Java exception, so the C++ flow is interrupted. Then, when +// translatePendingCppExceptionToJavaException is called at the +// topmost level of the native stack, the wrapped Java exception is +// thrown to the java caller. +template +[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) { + int msgSize = snprintf(nullptr, 0, fmt, args...); + + char *msg = (char*) alloca(msgSize + 1); + snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...); + throwNewJavaException(throwableName, msg); +} + +// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't +// be thrown, it aborts the program. This is a noexcept function at C++ level. +FBEXPORT void translatePendingCppExceptionToJavaException() noexcept; + +// For convenience, some exception names in java.lang are available here. + +const char* const gJavaLangIllegalArgumentException = "java/lang/IllegalArgumentException"; + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/File.h b/lib/fb/src/main/cpp/include/fb/fbjni/File.h new file mode 100644 index 00000000..74c752a0 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/File.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +class JFile : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Ljava/io/File;"; + + // Define a method that calls into the represented Java class + std::string getAbsolutePath() { + static auto method = getClass()->getMethod("getAbsolutePath"); + return method(self())->toStdString(); + } + +}; + +} +} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Hybrid.h b/lib/fb/src/main/cpp/include/fb/fbjni/Hybrid.h new file mode 100644 index 00000000..f020826c --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Hybrid.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include + +#include +#include + +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +namespace detail { + +class BaseHybridClass { +public: + virtual ~BaseHybridClass() {} +}; + +struct FBEXPORT HybridData : public JavaClass { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/HybridData;"; + void setNativePointer(std::unique_ptr new_value); + BaseHybridClass* getNativePointer(); + static local_ref create(); +}; + +template +struct HybridTraits { + // This static assert should actually always fail if we don't use one of the + // specializations below. + static_assert( + std::is_base_of::value || + std::is_base_of::value, + "The base of a HybridClass must be either another HybridClass or derived from JObject."); +}; + +template <> +struct HybridTraits { + using CxxBase = BaseHybridClass; + using JavaBase = JObject; +}; + +template +struct HybridTraits< + Base, + typename std::enable_if::value>::type> { + using CxxBase = Base; + using JavaBase = typename Base::JavaPart; +}; + +template +struct HybridTraits< + Base, + typename std::enable_if::value>::type> { + using CxxBase = BaseHybridClass; + using JavaBase = Base; +}; + +// convert to HybridClass* from jhybridobject +template +struct FBEXPORT Convert< + T, typename std::enable_if< + std::is_base_of::type>::value>::type> { + typedef typename std::remove_pointer::type::jhybridobject jniType; + static T fromJni(jniType t) { + if (t == nullptr) { + return nullptr; + } + return wrap_alias(t)->cthis(); + } + // There is no automatic return conversion for objects. +}; + +template +struct RefReprType::value, void>::type> { + static_assert(std::is_same::value, + "HybridFoo (where HybridFoo derives from HybridClass) is not supported in this context. " + "For an xxx_ref, you may want: xxx_ref or HybridFoo*."); + using Repr = T; +}; + + +} + +template +class FBEXPORT HybridClass : public detail::HybridTraits::CxxBase { +public: + struct JavaPart : JavaClass::JavaBase> { + // At this point, T is incomplete, and so we cannot access + // T::kJavaDescriptor directly. jtype_traits support this escape hatch for + // such a case. + static constexpr const char* kJavaDescriptor = nullptr; + static std::string get_instantiated_java_descriptor(); + static std::string get_instantiated_base_name(); + + using HybridType = T; + + // This will reach into the java object and extract the C++ instance from + // the mHybridData and return it. + T* cthis(); + + friend class HybridClass; + }; + + using jhybridobject = typename JavaPart::javaobject; + using javaobject = typename JavaPart::javaobject; + typedef detail::HybridData::javaobject jhybriddata; + + static alias_ref javaClassStatic() { + return JavaPart::javaClassStatic(); + } + + static local_ref javaClassLocal() { + std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2); + return findClassLocal(className.c_str()); + } + +protected: + typedef HybridClass HybridBase; + + // This ensures that a C++ hybrid part cannot be created on its own + // by default. If a hybrid wants to enable this, it can provide its + // own public ctor, or change the accessibility of this to public. + using detail::HybridTraits::CxxBase::CxxBase; + + static void registerHybrid(std::initializer_list methods) { + javaClassStatic()->registerNatives(methods); + } + + static local_ref makeHybridData(std::unique_ptr cxxPart) { + auto hybridData = detail::HybridData::create(); + hybridData->setNativePointer(std::move(cxxPart)); + return hybridData; + } + + template + static local_ref makeCxxInstance(Args&&... args) { + return makeHybridData(std::unique_ptr(new T(std::forward(args)...))); + } + +public: + // Factory method for creating a hybrid object where the arguments + // are used to initialize the C++ part directly without passing them + // through java. This method requires the Java part to have a ctor + // which takes a HybridData, and for the C++ part to have a ctor + // compatible with the arguments passed here. For safety, the ctor + // can be private, and the hybrid declared a friend of its base, so + // the hybrid can only be created from here. + // + // Exception behavior: This can throw an exception if creating the + // C++ object fails, or any JNI methods throw. + template + static local_ref newObjectCxxArgs(Args&&... args) { + auto hybridData = makeCxxInstance(std::forward(args)...); + return JavaPart::newInstance(hybridData); + } + + // TODO? Create reusable interface for Allocatable classes and use it to + // strengthen type-checking (and possibly provide a default + // implementation of allocate().) + template + static local_ref allocateWithCxxArgs(Args&&... args) { + auto hybridData = makeCxxInstance(std::forward(args)...); + static auto allocateMethod = + javaClassStatic()->template getStaticMethod("allocate"); + return allocateMethod(javaClassStatic(), hybridData.get()); + } + + // Factory method for creating a hybrid object where the arguments + // are passed to the java ctor. + template + static local_ref newObjectJavaArgs(Args&&... args) { + return JavaPart::newInstance(std::move(args)...); + } + + // If a hybrid class throws an exception which derives from + // std::exception, it will be passed to mapException on the hybrid + // class, or nearest ancestor. This allows boilerplate exception + // translation code (for example, calling throwNewJavaException on a + // particular java class) to be hoisted to a common function. If + // mapException returns, then the std::exception will be translated + // to Java. + static void mapException(const std::exception& ex) {} +}; + +template +inline T* HybridClass::JavaPart::cthis() { + static auto field = + HybridClass::JavaPart::javaClassStatic()->template getField("mHybridData"); + auto hybridData = this->getFieldValue(field); + if (!hybridData) { + throwNewJavaException("java/lang/NullPointerException", "java.lang.NullPointerException"); + } + // I'd like to use dynamic_cast here, but -fno-rtti is the default. + T* value = static_cast(hybridData->getNativePointer()); + // This would require some serious programmer error. + FBASSERTMSGF(value != 0, "Incorrect C++ type in hybrid field"); + return value; +}; + +template +/* static */ inline std::string HybridClass::JavaPart::get_instantiated_java_descriptor() { + return T::kJavaDescriptor; +} + +template +/* static */ inline std::string HybridClass::JavaPart::get_instantiated_base_name() { + auto name = get_instantiated_java_descriptor(); + return name.substr(1, name.size() - 2); +} + +// Given a *_ref object which refers to a hybrid class, this will reach inside +// of it, find the mHybridData, extract the C++ instance pointer, cast it to +// the appropriate type, and return it. +template +inline auto cthis(T jthis) -> decltype(jthis->cthis()) { + return jthis->cthis(); +} + +void HybridDataOnLoad(); + +} +} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Iterator-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/Iterator-inl.h new file mode 100644 index 00000000..c056e16a --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Iterator-inl.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace jni { + +namespace detail { + +template +struct IteratorHelper : public JavaClass> { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/IteratorHelper;"; + + typedef local_ref value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef std::forward_iterator_tag iterator_category; + + typedef JavaClass> JavaBase_; + + bool hasNext() const { + static auto hasNextMethod = + JavaBase_::javaClassStatic()->template getMethod("hasNext"); + return hasNextMethod(JavaBase_::self()); + } + + value_type next() { + static auto elementField = + JavaBase_::javaClassStatic()->template getField("mElement"); + return dynamic_ref_cast(JavaBase_::getFieldValue(elementField)); + } + + static void reset(value_type& v) { + v.reset(); + } +}; + +template +struct MapIteratorHelper : public JavaClass> { + constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/MapIteratorHelper;"; + + typedef std::pair, local_ref> value_type; + + typedef JavaClass> JavaBase_; + + bool hasNext() const { + static auto hasNextMethod = + JavaBase_::javaClassStatic()->template getMethod("hasNext"); + return hasNextMethod(JavaBase_::self()); + } + + value_type next() { + static auto keyField = JavaBase_::javaClassStatic()->template getField("mKey"); + static auto valueField = JavaBase_::javaClassStatic()->template getField("mValue"); + return std::make_pair(dynamic_ref_cast(JavaBase_::getFieldValue(keyField)), + dynamic_ref_cast(JavaBase_::getFieldValue(valueField))); + } + + static void reset(value_type& v) { + v.first.reset(); + v.second.reset(); + } +}; + +template +class Iterator { + public: + typedef typename T::value_type value_type; + typedef ptrdiff_t difference_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef std::input_iterator_tag iterator_category; + + // begin ctor + Iterator(global_ref&& helper) + : helper_(std::move(helper)) + , i_(-1) { + ++(*this); + } + + // end ctor + Iterator() + : i_(-1) {} + + bool operator==(const Iterator& it) const { return i_ == it.i_; } + bool operator!=(const Iterator& it) const { return !(*this == it); } + const value_type& operator*() const { assert(i_ != -1); return entry_; } + const value_type* operator->() const { assert(i_ != -1); return &entry_; } + Iterator& operator++() { // preincrement + bool hasNext = helper_->hasNext(); + if (hasNext) { + ++i_; + entry_ = helper_->next(); + } else { + i_ = -1; + helper_->reset(entry_); + } + return *this; + } + Iterator operator++(int) { // postincrement + Iterator ret; + ret.i_ = i_; + ret.entry_ = std::move(entry_); + ++(*this); + return ret; + } + + global_ref helper_; + // set to -1 at end + std::ptrdiff_t i_; + value_type entry_; +}; + +} + +template +struct JIterator::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +typename JIterator::Iterator JIterator::begin() const { + static auto ctor = detail::IteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JIterator::javaobject)>(); + return Iterator( + make_global( + detail::IteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JIterator::Iterator JIterator::end() const { + return Iterator(); +} + +template +struct JIterable::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +typename JIterable::Iterator JIterable::begin() const { + static auto ctor = detail::IteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JIterable::javaobject)>(); + return Iterator( + make_global( + detail::IteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JIterable::Iterator JIterable::end() const { + return Iterator(); +} + +template +size_t JCollection::size() const { + static auto sizeMethod = + JCollection::javaClassStatic()->template getMethod("size"); + return sizeMethod(this->self()); +} + +template +struct JMap::Iterator : public detail::Iterator> { + using detail::Iterator>::Iterator; +}; + +template +size_t JMap::size() const { + static auto sizeMethod = + JMap::javaClassStatic()->template getMethod("size"); + return sizeMethod(this->self()); +} + +template +typename JMap::Iterator JMap::begin() const { + static auto ctor = detail::MapIteratorHelper::javaClassStatic()-> + template getConstructor::javaobject( + typename JMap::javaobject)>(); + return Iterator( + make_global( + detail::MapIteratorHelper::javaClassStatic()->newObject(ctor, this->self()))); +} + +template +typename JMap::Iterator JMap::end() const { + return Iterator(); +} + +} +} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Iterator.h b/lib/fb/src/main/cpp/include/fb/fbjni/Iterator.h new file mode 100644 index 00000000..0da2c362 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Iterator.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +/** + * JavaClass which represents a reference to a java.util.Iterator instance. It + * provides begin()/end() methods to provide C++-style iteration over the + * underlying collection. The class has a template parameter for the element + * type, which defaults to jobject. For example: + * + * alias_ref::javaobject> my_iter = ...; + * + * In the simplest case, it can be used just as alias_ref::javaobject>, + * for example in a method declaration. + */ +template +struct JIterator : JavaClass> { + constexpr static auto kJavaDescriptor = "Ljava/util/Iterator;"; + + struct Iterator; + + /** + * To iterate: + * + * for (const auto& element : *jiter) { ... } + * + * The JIterator iterator value_type is local_ref, containing a reference + * to an element instance. + * + * If the Iterator returns objects whch are not convertible to the given + * element type, iteration will throw a java ClassCastException. + * + * For example, to convert an iterator over a collection of java strings to + * an std::vector of std::strings: + * + * std::vector vs; + * for (const auto& elem : *jiter) { + * vs.push_back(elem->toStdString()); + * } + * + * Or if you prefer using std algorithms: + * + * std::vector vs; + * std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs), + * [](const local_ref& elem) { return elem->toStdString(); }); + * + * The iterator is a InputIterator. + */ + Iterator begin() const; + Iterator end() const; +}; + +/** + * Similar to JIterator, except this represents any object which implements the + * java.lang.Iterable interface. It will create the Java Iterator as a part of + * begin(). + */ +template +struct JIterable : JavaClass> { + constexpr static auto kJavaDescriptor = "Ljava/lang/Iterable;"; + + struct Iterator; + + Iterator begin() const; + Iterator end() const; +}; + +/** + * JavaClass types which represent Collection, List, and Set are also provided. + * These preserve the Java class heirarchy. + */ +template +struct JCollection : JavaClass, JIterable> { + constexpr static auto kJavaDescriptor = "Ljava/util/Collection;"; + + /** + * Returns the number of elements in the collection. + */ + size_t size() const; +}; + +template +struct JList : JavaClass, JCollection> { + constexpr static auto kJavaDescriptor = "Ljava/util/List;"; +}; + +template +struct JSet : JavaClass, JCollection> { + constexpr static auto kJavaDescriptor = "Ljava/util/Set;"; +}; + +/** + * JavaClass which represents a reference to a java.util.Map instance. It adds + * wrappers around Java methods, including begin()/end() methods to provide + * C++-style iteration over the Java Map. The class has template parameters + * for the key and value types, which default to jobject. For example: + * + * alias_ref::javaobject> my_map = ...; + * + * In the simplest case, it can be used just as alias_ref::javaobject>, + * for example in a method declaration. + */ +template +struct JMap : JavaClass> { + constexpr static auto kJavaDescriptor = "Ljava/util/Map;"; + + struct Iterator; + + /** + * Returns the number of pairs in the map. + */ + size_t size() const; + + /** + * To iterate over the Map: + * + * for (const auto& entry : *jmap) { ... } + * + * The JMap iterator value_type is std::pair, local_ref> + * containing references to key and value instances. + * + * If the Map contains objects whch are not convertible to the given key and + * value types, iteration will throw a java ClassCastException. + * + * The iterator is a InputIterator. + */ + Iterator begin() const; + Iterator end() const; +}; + +} +} + +#include "Iterator-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/JThread.h b/lib/fb/src/main/cpp/include/fb/fbjni/JThread.h new file mode 100644 index 00000000..1bb61199 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/JThread.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" +#include "NativeRunnable.h" + +namespace facebook { +namespace jni { + +class JThread : public JavaClass { + public: + static constexpr const char* kJavaDescriptor = "Ljava/lang/Thread;"; + + void start() { + static auto method = javaClassStatic()->getMethod("start"); + method(self()); + } + + void join() { + static auto method = javaClassStatic()->getMethod("join"); + method(self()); + } + + static local_ref create(std::function&& runnable) { + auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable)); + return newInstance(static_ref_cast(jrunnable)); + } +}; + +} +} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Meta-forward.h b/lib/fb/src/main/cpp/include/fb/fbjni/Meta-forward.h new file mode 100644 index 00000000..60dfee44 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Meta-forward.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +namespace facebook { +namespace jni { + +template +class JMethod; +template +class JStaticMethod; +template +class JNonvirtualMethod; +template +struct JConstructor; +template +class JField; +template +class JStaticField; + +/// Type traits for Java types (currently providing Java type descriptors) +template +struct jtype_traits; + +/// Type traits for Java methods (currently providing Java type descriptors) +template +struct jmethod_traits; + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Meta-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/Meta-inl.h new file mode 100644 index 00000000..ff4c016b --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Meta-inl.h @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "Common.h" +#include "Exceptions.h" +#include "MetaConvert.h" +#include "References.h" +#include "Boxed.h" + +#if defined(__ANDROID__) +#include +#endif + +namespace facebook { +namespace jni { + +// JMethod ///////////////////////////////////////////////////////////////////////////////////////// + +inline JMethodBase::JMethodBase(jmethodID method_id) noexcept + : method_id_{method_id} +{} + +inline JMethodBase::operator bool() const noexcept { + return method_id_ != nullptr; +} + +inline jmethodID JMethodBase::getId() const noexcept { + return method_id_; +} + +namespace { + +template +struct ArgsArraySetter; + +template +struct ArgsArraySetter { + static void set(alias_ref::javaobject> array, Arg arg0, Args... args) { + // TODO(xxxxxxxx): Use Convert... to do conversions like the fast path. + (*array)[idx] = autobox(arg0); + ArgsArraySetter::set(array, args...); + } +}; + +template +struct ArgsArraySetter { + static void set(alias_ref::javaobject> array) { + } +}; + +template +local_ref::javaobject> makeArgsArray(Args... args) { + auto arr = JArrayClass::newArray(sizeof...(args)); + ArgsArraySetter<0, Args...>::set(arr, args...); + return arr; +} + + +inline bool needsSlowPath(alias_ref obj) { +#if defined(__ANDROID__) + // On Android 6.0, art crashes when attempting to call a function on a Proxy. + // So, when we detect that case we must use the safe, slow workaround. That is, + // we resolve the method id to the corresponding java.lang.reflect.Method object + // and make the call via it's invoke() method. + static auto android_sdk = ([] { + char sdk_version_str[PROP_VALUE_MAX]; + __system_property_get("ro.build.version.sdk", sdk_version_str); + return atoi(sdk_version_str); + })(); + static auto is_bad_android = android_sdk == 23; + if (!is_bad_android) return false; + static auto proxy_class = findClassStatic("java/lang/reflect/Proxy"); + return obj->isInstanceOf(proxy_class); +#else + return false; +#endif +} + +} + +template +inline void JMethod::operator()(alias_ref self, Args... args) { + const auto env = Environment::current(); + env->CallVoidMethod( + self.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); +} + +#pragma push_macro("DEFINE_PRIMITIVE_CALL") +#undef DEFINE_PRIMITIVE_CALL +#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD, CLASS) \ +template \ +inline TYPE JMethod::operator()(alias_ref self, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->Call ## METHOD ## Method( \ + self.get(), \ + getId(), \ + detail::callToJni(detail::Convert::type>::toCall(args))...); \ + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ + return result; \ +} + +DEFINE_PRIMITIVE_CALL(jboolean, Boolean, JBoolean) +DEFINE_PRIMITIVE_CALL(jbyte, Byte, JByte) +DEFINE_PRIMITIVE_CALL(jchar, Char, JCharacter) +DEFINE_PRIMITIVE_CALL(jshort, Short, JShort) +DEFINE_PRIMITIVE_CALL(jint, Int, JInteger) +DEFINE_PRIMITIVE_CALL(jlong, Long, JLong) +DEFINE_PRIMITIVE_CALL(jfloat, Float, JFloat) +DEFINE_PRIMITIVE_CALL(jdouble, Double, JDouble) +#pragma pop_macro("DEFINE_PRIMITIVE_CALL") + +/// JMethod specialization for references that wraps the return value in a @ref local_ref +template +class JMethod : public JMethodBase { + public: + // TODO: static_assert is jobject-derived or local_ref jobject + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "JniRet must be a JNI reference"); + using JMethodBase::JMethodBase; + JMethod() noexcept {}; + JMethod(const JMethod& other) noexcept = default; + + /// Invoke a method and return a local reference wrapping the result + local_ref operator()(alias_ref self, Args... args); + + friend class JClass; +}; + +template +inline auto JMethod::operator()(alias_ref self, Args... args) -> local_ref { + const auto env = Environment::current(); + auto result = env->CallObjectMethod( + self.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(result)); +} + +template +inline void JStaticMethod::operator()(alias_ref cls, Args... args) { + const auto env = internal::getEnv(); + env->CallStaticVoidMethod( + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); +} + +#pragma push_macro("DEFINE_PRIMITIVE_STATIC_CALL") +#undef DEFINE_PRIMITIVE_STATIC_CALL +#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD) \ +template \ +inline TYPE JStaticMethod::operator()(alias_ref cls, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->CallStatic ## METHOD ## Method( \ + cls.get(), \ + getId(), \ + detail::callToJni(detail::Convert::type>::toCall(args))...); \ + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ + return result; \ +} + +DEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean) +DEFINE_PRIMITIVE_STATIC_CALL(jbyte, Byte) +DEFINE_PRIMITIVE_STATIC_CALL(jchar, Char) +DEFINE_PRIMITIVE_STATIC_CALL(jshort, Short) +DEFINE_PRIMITIVE_STATIC_CALL(jint, Int) +DEFINE_PRIMITIVE_STATIC_CALL(jlong, Long) +DEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float) +DEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double) +#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_CALL") + +/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref +template +class JStaticMethod : public JMethodBase { + + public: + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "T* must be a JNI reference"); + using JMethodBase::JMethodBase; + JStaticMethod() noexcept {}; + JStaticMethod(const JStaticMethod& other) noexcept = default; + + /// Invoke a method and return a local reference wrapping the result + local_ref operator()(alias_ref cls, Args... args) { + const auto env = internal::getEnv(); + auto result = env->CallStaticObjectMethod( + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(result)); + } + + friend class JClass; +}; + +template +inline void +JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) { + const auto env = internal::getEnv(); + env->CallNonvirtualVoidMethod( + self.get(), + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); +} + +#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL") +#undef DEFINE_PRIMITIVE_NON_VIRTUAL_CALL +#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD) \ +template \ +inline TYPE \ +JNonvirtualMethod::operator()(alias_ref self, alias_ref cls, Args... args) { \ + const auto env = internal::getEnv(); \ + auto result = env->CallNonvirtual ## METHOD ## Method( \ + self.get(), \ + cls.get(), \ + getId(), \ + detail::callToJni(detail::Convert::type>::toCall(args))...); \ + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); \ + return result; \ +} + +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jbyte, Byte) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jchar, Char) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jshort, Short) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jint, Int) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jlong, Long) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float) +DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double) +#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_CALL") + +/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref +template +class JNonvirtualMethod : public JMethodBase { + public: + using JniRet = typename detail::Convert::type>::jniType; + static_assert(IsPlainJniReference(), "T* must be a JNI reference"); + using JMethodBase::JMethodBase; + JNonvirtualMethod() noexcept {}; + JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; + + /// Invoke a method and return a local reference wrapping the result + local_ref operator()(alias_ref self, alias_ref cls, Args... args){ + const auto env = internal::getEnv(); + auto result = env->CallNonvirtualObjectMethod( + self.get(), + cls.get(), + getId(), + detail::callToJni(detail::Convert::type>::toCall(args))...); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return adopt_local(static_cast(result)); + } + + friend class JClass; +}; + +template +local_ref slowCall(jmethodID method_id, alias_ref self, Args... args) { + static auto invoke = findClassStatic("java/lang/reflect/Method") + ->getMethod::javaobject)>("invoke"); + // TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod. + auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE)); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + if (!reflected) throw std::runtime_error("Unable to get reflected java.lang.reflect.Method"); + auto argsArray = makeArgsArray(args...); + // No need to check for exceptions since invoke is itself a JMethod that will do that for us. + return invoke(reflected, self.get(), argsArray.get()); +} + + +// JField /////////////////////////////////////////////////////////////////////////////////////// + +template +inline JField::JField(jfieldID field) noexcept + : field_id_{field} +{} + +template +inline JField::operator bool() const noexcept { + return field_id_ != nullptr; +} + +template +inline jfieldID JField::getId() const noexcept { + return field_id_; +} + +#pragma push_macro("DEFINE_FIELD_PRIMITIVE_GET_SET") +#undef DEFINE_FIELD_PRIMITIVE_GET_SET +#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ +template<> \ +inline TYPE JField::get(jobject object) const noexcept { \ + const auto env = internal::getEnv(); \ + return env->Get ## METHOD ## Field(object, field_id_); \ +} \ + \ +template<> \ +inline void JField::set(jobject object, TYPE value) noexcept { \ + const auto env = internal::getEnv(); \ + env->Set ## METHOD ## Field(object, field_id_, value); \ +} + +DEFINE_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean) +DEFINE_FIELD_PRIMITIVE_GET_SET(jbyte, Byte) +DEFINE_FIELD_PRIMITIVE_GET_SET(jchar, Char) +DEFINE_FIELD_PRIMITIVE_GET_SET(jshort, Short) +DEFINE_FIELD_PRIMITIVE_GET_SET(jint, Int) +DEFINE_FIELD_PRIMITIVE_GET_SET(jlong, Long) +DEFINE_FIELD_PRIMITIVE_GET_SET(jfloat, Float) +DEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double) +#pragma pop_macro("DEFINE_FIELD_PRIMITIVE_GET_SET") + +template +inline T JField::get(jobject object) const noexcept { + return static_cast(internal::getEnv()->GetObjectField(object, field_id_)); +} + +template +inline void JField::set(jobject object, T value) noexcept { + internal::getEnv()->SetObjectField(object, field_id_, static_cast(value)); +} + +// JStaticField ///////////////////////////////////////////////////////////////////////////////// + +template +inline JStaticField::JStaticField(jfieldID field) noexcept + : field_id_{field} +{} + +template +inline JStaticField::operator bool() const noexcept { + return field_id_ != nullptr; +} + +template +inline jfieldID JStaticField::getId() const noexcept { + return field_id_; +} + +#pragma push_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET") +#undef DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET +#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD) \ +template<> \ +inline TYPE JStaticField::get(jclass jcls) const noexcept { \ + const auto env = internal::getEnv(); \ + return env->GetStatic ## METHOD ## Field(jcls, field_id_); \ +} \ + \ +template<> \ +inline void JStaticField::set(jclass jcls, TYPE value) noexcept { \ + const auto env = internal::getEnv(); \ + env->SetStatic ## METHOD ## Field(jcls, field_id_, value); \ +} + +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jbyte, Byte) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jchar, Char) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jshort, Short) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jint, Int) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jlong, Long) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jfloat, Float) +DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double) +#pragma pop_macro("DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET") + +template +inline T JStaticField::get(jclass jcls) const noexcept { + const auto env = internal::getEnv(); + return static_cast(env->GetStaticObjectField(jcls, field_id_)); +} + +template +inline void JStaticField::set(jclass jcls, T value) noexcept { + internal::getEnv()->SetStaticObjectField(jcls, field_id_, value); +} + + +// jmethod_traits ////////////////////////////////////////////////////////////////////////////////// + +// TODO(T6608405) Adapt this to implement a register natives method that requires no descriptor +namespace internal { + +template +inline std::string JavaDescriptor() { + return jtype_traits::descriptor(); +} + +template +inline std::string JavaDescriptor() { + return JavaDescriptor() + JavaDescriptor(); +} + +template +inline std::string JMethodDescriptor() { + return "(" + JavaDescriptor() + ")" + JavaDescriptor(); +} + +template +inline std::string JMethodDescriptor() { + return "()" + JavaDescriptor(); +} + +} // internal + +template +inline std::string jmethod_traits::descriptor() { + return internal::JMethodDescriptor(); +} + +template +inline std::string jmethod_traits::constructor_descriptor() { + return internal::JMethodDescriptor(); +} + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Meta.h b/lib/fb/src/main/cpp/include/fb/fbjni/Meta.h new file mode 100644 index 00000000..e9717bf7 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Meta.h @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** @file meta.h + * + * Provides wrappers for meta data such as methods and fields. + */ + +#pragma once + +#include +#include + +#include + +#include "References-forward.h" + +#ifdef __ANDROID__ +# include +# define XLOG_TAG "fb-jni" +# define XLOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, XLOG_TAG, __VA_ARGS__) +# define XLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, XLOG_TAG, __VA_ARGS__) +# define XLOGI(...) __android_log_print(ANDROID_LOG_INFO, XLOG_TAG, __VA_ARGS__) +# define XLOGW(...) __android_log_print(ANDROID_LOG_WARN, XLOG_TAG, __VA_ARGS__) +# define XLOGE(...) __android_log_print(ANDROID_LOG_ERROR, XLOG_TAG, __VA_ARGS__) +# define XLOGWTF(...) __android_log_print(ANDROID_LOG_FATAL, XLOG_TAG, __VA_ARGS__) +#endif + +namespace facebook { +namespace jni { + +// This will get the reflected Java Method from the method_id, get it's invoke +// method, and call the method via that. This shouldn't ever be needed, but +// Android 6.0 crashes when calling a method on a java.lang.Proxy via jni. +template +local_ref slowCall(jmethodID method_id, alias_ref self, Args... args); + +class JObject; + + +/// Wrapper of a jmethodID. Provides a common base for JMethod specializations +class JMethodBase { + public: + /// Verify that the method is valid + explicit operator bool() const noexcept; + + /// Access the wrapped id + jmethodID getId() const noexcept; + + protected: + /// Create a wrapper of a method id + explicit JMethodBase(jmethodID method_id = nullptr) noexcept; + + private: + jmethodID method_id_; +}; + + +/// Representation of a jmethodID +template +class JMethod; + +/// @cond INTERNAL +#pragma push_macro("DEFINE_PRIMITIVE_METHOD_CLASS") + +#undef DEFINE_PRIMITIVE_METHOD_CLASS + +// Defining JMethod specializations based on return value +#define DEFINE_PRIMITIVE_METHOD_CLASS(TYPE) \ +template \ +class JMethod : public JMethodBase { \ + public: \ + static_assert(std::is_void::value || IsJniPrimitive(), \ + "TYPE must be primitive or void"); \ + \ + using JMethodBase::JMethodBase; \ + JMethod() noexcept {}; \ + JMethod(const JMethod& other) noexcept = default; \ + \ + TYPE operator()(alias_ref self, Args... args); \ + \ + friend class JClass; \ +} + +DEFINE_PRIMITIVE_METHOD_CLASS(void); +DEFINE_PRIMITIVE_METHOD_CLASS(jboolean); +DEFINE_PRIMITIVE_METHOD_CLASS(jbyte); +DEFINE_PRIMITIVE_METHOD_CLASS(jchar); +DEFINE_PRIMITIVE_METHOD_CLASS(jshort); +DEFINE_PRIMITIVE_METHOD_CLASS(jint); +DEFINE_PRIMITIVE_METHOD_CLASS(jlong); +DEFINE_PRIMITIVE_METHOD_CLASS(jfloat); +DEFINE_PRIMITIVE_METHOD_CLASS(jdouble); + +#pragma pop_macro("DEFINE_PRIMITIVE_METHOD_CLASS") +/// @endcond + + +/// Convenience type representing constructors +/// These should only be used with JClass::getConstructor and JClass::newObject. +template +struct JConstructor : private JMethod { + using JMethod::JMethod; + private: + JConstructor(const JMethod& other) : JMethod(other.getId()) {} + friend class JClass; +}; + +/// Representation of a jStaticMethodID +template +class JStaticMethod; + +/// @cond INTERNAL +#pragma push_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS") + +#undef DEFINE_PRIMITIVE_STATIC_METHOD_CLASS + +// Defining JStaticMethod specializations based on return value +#define DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(TYPE) \ +template \ +class JStaticMethod : public JMethodBase { \ + static_assert(std::is_void::value || IsJniPrimitive(), \ + "T must be a JNI primitive or void"); \ + \ + public: \ + using JMethodBase::JMethodBase; \ + JStaticMethod() noexcept {}; \ + JStaticMethod(const JStaticMethod& other) noexcept = default; \ + \ + TYPE operator()(alias_ref cls, Args... args); \ + \ + friend class JClass; \ +} + +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jboolean); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jbyte); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jchar); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jshort); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jint); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jlong); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jfloat); +DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble); + +#pragma pop_macro("DEFINE_PRIMITIVE_STATIC_METHOD_CLASS") +/// @endcond + + +/// Representation of a jNonvirtualMethodID +template +class JNonvirtualMethod; + +/// @cond INTERNAL +#pragma push_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS") + +#undef DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS + +// Defining JNonvirtualMethod specializations based on return value +#define DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(TYPE) \ +template \ +class JNonvirtualMethod : public JMethodBase { \ + static_assert(std::is_void::value || IsJniPrimitive(), \ + "T must be a JNI primitive or void"); \ + \ + public: \ + using JMethodBase::JMethodBase; \ + JNonvirtualMethod() noexcept {}; \ + JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default; \ + \ + TYPE operator()(alias_ref self, alias_ref cls, Args... args); \ + \ + friend class JClass; \ +} + +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jboolean); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jbyte); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jchar); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jshort); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jint); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jlong); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jfloat); +DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble); + +#pragma pop_macro("DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS") +/// @endcond + + +/** + * JField represents typed fields and simplifies their access. Note that object types return + * raw pointers which generally should promptly get a wrap_local treatment. + */ +template +class JField { + static_assert(IsJniScalar(), "T must be a JNI scalar"); + + public: + /// Wraps an existing field id + explicit JField(jfieldID field = nullptr) noexcept; + + /// Verify that the id is valid + explicit operator bool() const noexcept; + + /// Access the wrapped id + jfieldID getId() const noexcept; + + private: + jfieldID field_id_; + + /// Get field value + /// @pre object != nullptr + T get(jobject object) const noexcept; + + /// Set field value + /// @pre object != nullptr + void set(jobject object, T value) noexcept; + + friend class JObject; +}; + + +/** + * JStaticField represents typed fields and simplifies their access. Note that object types + * return raw pointers which generally should promptly get a wrap_local treatment. + */ +template +class JStaticField { + static_assert(IsJniScalar(), "T must be a JNI scalar"); + + public: + /// Wraps an existing field id + explicit JStaticField(jfieldID field = nullptr) noexcept; + + /// Verify that the id is valid + explicit operator bool() const noexcept; + + /// Access the wrapped id + jfieldID getId() const noexcept; + + private: + jfieldID field_id_; + + /// Get field value + /// @pre object != nullptr + T get(jclass jcls) const noexcept; + + /// Set field value + /// @pre object != nullptr + void set(jclass jcls, T value) noexcept; + + friend class JClass; + friend class JObject; +}; + + +/// Template magic to provide @ref jmethod_traits +template +struct jmethod_traits { + static std::string descriptor(); + static std::string constructor_descriptor(); +}; + + +// jtype_traits //////////////////////////////////////////////////////////////////////////////////// + +template +struct jtype_traits { +private: + using Repr = ReprType; +public: + // The jni type signature (described at + // http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html). + static std::string descriptor() { + std::string descriptor; + if (Repr::kJavaDescriptor == nullptr) { + descriptor = Repr::get_instantiated_java_descriptor(); + } else { + descriptor = Repr::kJavaDescriptor; + } + return descriptor; + } + + // The signature used for class lookups. See + // http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName(). + static std::string base_name() { + if (Repr::kJavaDescriptor != nullptr) { + std::string base_name = Repr::kJavaDescriptor; + return base_name.substr(1, base_name.size() - 2); + } + return Repr::get_instantiated_base_name(); + } +}; + +#pragma push_macro("DEFINE_FIELD_AND_ARRAY_TRAIT") +#undef DEFINE_FIELD_AND_ARRAY_TRAIT + +#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC) \ +template<> \ +struct jtype_traits { \ + static std::string descriptor() { return std::string{#DSC}; } \ + static std::string base_name() { return descriptor(); } \ + using array_type = TYPE ## Array; \ +}; \ +template<> \ +struct jtype_traits { \ + static std::string descriptor() { return std::string{"[" #DSC}; } \ + static std::string base_name() { return descriptor(); } \ + using entry_type = TYPE; \ +}; + +// There is no voidArray, handle that without the macro. +template<> +struct jtype_traits { + static std::string descriptor() { return std::string{"V"}; }; +}; + +DEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z) +DEFINE_FIELD_AND_ARRAY_TRAIT(jbyte, B) +DEFINE_FIELD_AND_ARRAY_TRAIT(jchar, C) +DEFINE_FIELD_AND_ARRAY_TRAIT(jshort, S) +DEFINE_FIELD_AND_ARRAY_TRAIT(jint, I) +DEFINE_FIELD_AND_ARRAY_TRAIT(jlong, J) +DEFINE_FIELD_AND_ARRAY_TRAIT(jfloat, F) +DEFINE_FIELD_AND_ARRAY_TRAIT(jdouble, D) + +#pragma pop_macro("DEFINE_FIELD_AND_ARRAY_TRAIT") + + +template +struct jmethod_traits_from_cxx; + +}} + +#include "Meta-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/MetaConvert.h b/lib/fb/src/main/cpp/include/fb/fbjni/MetaConvert.h new file mode 100644 index 00000000..33027c7e --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/MetaConvert.h @@ -0,0 +1,122 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include + +#include "Common.h" +#include "References.h" + +namespace facebook { +namespace jni { + +namespace detail { + +// In order to avoid potentially filling the jni locals table, +// temporary objects (right now, this is just jstrings) need to be +// released. This is done by returning a holder which autoconverts to +// jstring. +template +inline T callToJni(T&& t) { + return t; +} + +template +inline JniType callToJni(local_ref&& sref) { + return sref.get(); +} + +// Normally, pass through types unmolested. +template +struct Convert { + typedef T jniType; + static jniType fromJni(jniType t) { + return t; + } + static jniType toJniRet(jniType t) { + return t; + } + static jniType toCall(jniType t) { + return t; + } +}; + +// This is needed for return conversion +template <> +struct Convert { + typedef void jniType; +}; + +// jboolean is an unsigned char, not a bool. Allow it to work either way. +template<> +struct Convert { + typedef jboolean jniType; + static bool fromJni(jniType t) { + return t; + } + static jniType toJniRet(bool t) { + return t; + } + static jniType toCall(bool t) { + return t; + } +}; + +// convert to alias_ref from T +template +struct Convert> { + typedef JniType jniType; + static alias_ref fromJni(jniType t) { + return wrap_alias(t); + } + static jniType toJniRet(alias_ref t) { + return t.get(); + } + static jniType toCall(alias_ref t) { + return t.get(); + } +}; + +// convert return from local_ref +template +struct Convert> { + typedef JniType jniType; + // No automatic synthesis of local_ref + static jniType toJniRet(local_ref t) { + return t.release(); + } + static jniType toCall(local_ref t) { + return t.get(); + } +}; + +// convert return from global_ref +template +struct Convert> { + typedef JniType jniType; + // No automatic synthesis of global_ref + static jniType toJniRet(global_ref t) { + return t.get(); + } + static jniType toCall(global_ref t) { + return t.get(); + } +}; + +template struct jni_sig_from_cxx_t; +template +struct jni_sig_from_cxx_t { + using JniRet = typename Convert::type>::jniType; + using JniSig = JniRet(typename Convert::type>::jniType...); +}; + +template +using jni_sig_from_cxx = typename jni_sig_from_cxx_t::JniSig; + +} // namespace detail + +template +struct jmethod_traits_from_cxx : jmethod_traits> { +}; + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/NativeRunnable.h b/lib/fb/src/main/cpp/include/fb/fbjni/NativeRunnable.h new file mode 100644 index 00000000..0502e36b --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/NativeRunnable.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "CoreClasses.h" +#include "Hybrid.h" +#include "Registration.h" + +#include + +namespace facebook { +namespace jni { + +struct JRunnable : public JavaClass { + static auto constexpr kJavaDescriptor = "Ljava/lang/Runnable;"; +}; + +struct JNativeRunnable : public HybridClass { + public: + static auto constexpr kJavaDescriptor = "Lcom/facebook/jni/NativeRunnable;"; + + JNativeRunnable(std::function&& runnable) : runnable_(std::move(runnable)) {} + + static void OnLoad() { + registerHybrid({ + makeNativeMethod("run", JNativeRunnable::run), + }); + } + + void run() { + runnable_(); + } + + private: + std::function runnable_; +}; + + +} // namespace jni +} // namespace facebook diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h new file mode 100644 index 00000000..65b9828c --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators-inl.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace jni { + +/// @cond INTERNAL +namespace internal { + +// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined) +struct ReferenceStats { + std::atomic_uint locals_deleted, globals_deleted, weaks_deleted; + + void reset() noexcept; +}; + +extern ReferenceStats g_reference_stats; +} +/// @endcond + + +// LocalReferenceAllocator ///////////////////////////////////////////////////////////////////////// + +inline jobject LocalReferenceAllocator::newReference(jobject original) const { + internal::dbglog("Local new: %p", original); + auto ref = internal::getEnv()->NewLocalRef(original); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return ref; +} + +inline void LocalReferenceAllocator::deleteReference(jobject reference) const noexcept { + internal::dbglog("Local release: %p", reference); + + if (reference) { + #ifdef FBJNI_DEBUG_REFS + ++internal::g_reference_stats.locals_deleted; + #endif + assert(verifyReference(reference)); + internal::getEnv()->DeleteLocalRef(reference); + } +} + +inline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept { + if (!reference || !internal::doesGetObjectRefTypeWork()) { + return true; + } + return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType; +} + + +// GlobalReferenceAllocator //////////////////////////////////////////////////////////////////////// + +inline jobject GlobalReferenceAllocator::newReference(jobject original) const { + internal::dbglog("Global new: %p", original); + auto ref = internal::getEnv()->NewGlobalRef(original); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return ref; +} + +inline void GlobalReferenceAllocator::deleteReference(jobject reference) const noexcept { + internal::dbglog("Global release: %p", reference); + + if (reference) { + #ifdef FBJNI_DEBUG_REFS + ++internal::g_reference_stats.globals_deleted; + #endif + assert(verifyReference(reference)); + internal::getEnv()->DeleteGlobalRef(reference); + } +} + +inline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { + if (!reference || !internal::doesGetObjectRefTypeWork()) { + return true; + } + return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType; +} + + +// WeakGlobalReferenceAllocator //////////////////////////////////////////////////////////////////// + +inline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const { + internal::dbglog("Weak global new: %p", original); + auto ref = internal::getEnv()->NewWeakGlobalRef(original); + FACEBOOK_JNI_THROW_PENDING_EXCEPTION(); + return ref; +} + +inline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) const noexcept { + internal::dbglog("Weak Global release: %p", reference); + + if (reference) { + #ifdef FBJNI_DEBUG_REFS + ++internal::g_reference_stats.weaks_deleted; + #endif + assert(verifyReference(reference)); + internal::getEnv()->DeleteWeakGlobalRef(reference); + } +} + +inline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept { + if (!reference || !internal::doesGetObjectRefTypeWork()) { + return true; + } + return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType; +} + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h b/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h new file mode 100644 index 00000000..a971263a --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/ReferenceAllocators.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * @file ReferenceAllocators.h + * + * Reference allocators are used to create and delete various classes of JNI references (local, + * global, and weak global). + */ + +#pragma once + +#include + +#include "Common.h" + +namespace facebook { namespace jni { + +/// Allocator that handles local references +class FBEXPORT LocalReferenceAllocator { + public: + jobject newReference(jobject original) const; + void deleteReference(jobject reference) const noexcept; + bool verifyReference(jobject reference) const noexcept; +}; + +/// Allocator that handles global references +class FBEXPORT GlobalReferenceAllocator { + public: + jobject newReference(jobject original) const; + void deleteReference(jobject reference) const noexcept; + bool verifyReference(jobject reference) const noexcept; +}; + +/// Allocator that handles weak global references +class FBEXPORT WeakGlobalReferenceAllocator { + public: + jobject newReference(jobject original) const; + void deleteReference(jobject reference) const noexcept; + bool verifyReference(jobject reference) const noexcept; +}; + +/// @cond INTERNAL +namespace internal { + +/** + * @return true iff env->GetObjectRefType is expected to work properly. + */ +FBEXPORT bool doesGetObjectRefTypeWork(); + +} +/// @endcond + +}} + +#include "ReferenceAllocators-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/References-forward.h b/lib/fb/src/main/cpp/include/fb/fbjni/References-forward.h new file mode 100644 index 00000000..58ce511d --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/References-forward.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "ReferenceAllocators.h" + +namespace facebook { +namespace jni { + +template +class JObjectWrapper; + +namespace detail { +struct JObjectBase { + jobject get() const noexcept; + void set(jobject reference) noexcept; + jobject this_; +}; + +// RefReprType maps a type to the representation used by fbjni smart references. +template +struct RefReprType; + +template +struct JavaObjectType; + +template +struct ReprAccess; +} + +// Given T, either a jobject-like type or a JavaClass-derived type, ReprType +// is the corresponding JavaClass-derived type and JniType is the +// jobject-like type. +template +using ReprType = typename detail::RefReprType::type; + +template +using JniType = typename detail::JavaObjectType::type; + +template +class base_owned_ref; + +template +class basic_strong_ref; + +template +class weak_ref; + +template +class alias_ref; + +/// A smart unique reference owning a local JNI reference +template +using local_ref = basic_strong_ref; + +/// A smart unique reference owning a global JNI reference +template +using global_ref = basic_strong_ref; + +}} // namespace facebook::jni diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/References-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/References-inl.h new file mode 100644 index 00000000..6bdc1815 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/References-inl.h @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include "CoreClasses.h" + +namespace facebook { +namespace jni { + +template +inline enable_if_t(), T> getPlainJniReference(T ref) { + return ref; +} + +template +inline JniType getPlainJniReference(alias_ref ref) { + return ref.get(); +} + +template +inline JniType getPlainJniReference(const base_owned_ref& ref) { + return ref.get(); +} + + +namespace detail { +template +struct ReprAccess { + using javaobject = JniType; + static void set(Repr& repr, javaobject obj) noexcept { + repr.JObjectBase::set(obj); + } + static javaobject get(const Repr& repr) { + return static_cast(repr.JObject::get()); + } +}; + +namespace { +template +void StaticAssertValidRepr() noexcept { + static_assert(std::is_base_of::value, + "A smart ref representation must be derived from JObject."); + static_assert(IsPlainJniReference>(), "T must be a JNI reference"); + static_assert(sizeof(Repr) == sizeof(JObjectBase), ""); + static_assert(alignof(Repr) == alignof(JObjectBase), ""); +} +} + +template +ReprStorage::ReprStorage(JniType obj) noexcept { + StaticAssertValidRepr(); + set(obj); +} + +template +void ReprStorage::set(JniType obj) noexcept { + new (&storage_) Repr; + ReprAccess::set(get(), obj); +} + +template +Repr& ReprStorage::get() noexcept { + return *reinterpret_cast(&storage_); +} + +template +const Repr& ReprStorage::get() const noexcept { + return *reinterpret_cast(&storage_); +} + +template +JniType ReprStorage::jobj() const noexcept { + ReprAccess::get(get()); + return ReprAccess::get(get()); +} + +template +void ReprStorage::swap(ReprStorage& other) noexcept { + StaticAssertValidRepr(); + using std::swap; + swap(get(), other.get()); +} + +inline void JObjectBase::set(jobject reference) noexcept { + this_ = reference; +} + +inline jobject JObjectBase::get() const noexcept { + return this_; +} + +template +enable_if_t(), plain_jni_reference_t> make_ref(const T& reference) { + auto old_reference = getPlainJniReference(reference); + if (!old_reference) { + return nullptr; + } + + auto ref = Alloc{}.newReference(old_reference); + if (!ref) { + // Note that we end up here if we pass a weak ref that refers to a collected object. + // Thus, it's hard to come up with a reason why this function should be used with + // weak references. + throw std::bad_alloc{}; + } + + return static_cast>(ref); +} + +} // namespace detail + +template +inline local_ref adopt_local(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return local_ref{ref}; +} + +template +inline global_ref adopt_global(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return global_ref{ref}; +} + +template +inline weak_ref adopt_weak_global(T ref) noexcept { + static_assert(IsPlainJniReference(), "T must be a plain jni reference"); + return weak_ref{ref}; +} + + +template +inline enable_if_t(), alias_ref> wrap_alias(T ref) noexcept { + return alias_ref(ref); +} + + +template +enable_if_t(), alias_ref> wrap_alias(T ref) noexcept; + + +template +enable_if_t(), local_ref>> +make_local(const T& ref) { + return adopt_local(detail::make_ref(ref)); +} + +template +enable_if_t(), global_ref>> +make_global(const T& ref) { + return adopt_global(detail::make_ref(ref)); +} + +template +enable_if_t(), weak_ref>> +make_weak(const T& ref) { + return adopt_weak_global(detail::make_ref(ref)); +} + +template +inline enable_if_t() && IsNonWeakReference(), bool> +operator==(const T1& a, const T2& b) { + return isSameObject(getPlainJniReference(a), getPlainJniReference(b)); +} + +template +inline enable_if_t() && IsNonWeakReference(), bool> +operator!=(const T1& a, const T2& b) { + return !(a == b); +} + + +// base_owned_ref /////////////////////////////////////////////////////////////////////// + +template +inline base_owned_ref::base_owned_ref() noexcept + : base_owned_ref(nullptr) +{} + +template +inline base_owned_ref::base_owned_ref(std::nullptr_t t) noexcept + : base_owned_ref(static_cast(nullptr)) +{} + +template +inline base_owned_ref::base_owned_ref(const base_owned_ref& other) + : storage_{static_cast(Alloc{}.newReference(other.get()))} +{} + +template +template +inline base_owned_ref::base_owned_ref(const base_owned_ref& other) + : storage_{static_cast(Alloc{}.newReference(other.get()))} +{ + static_assert(std::is_convertible, javaobject>::value, ""); +} + +template +inline facebook::jni::base_owned_ref::base_owned_ref( + javaobject reference) noexcept + : storage_(reference) { + assert(Alloc{}.verifyReference(reference)); + internal::dbglog("New wrapped ref=%p this=%p", get(), this); +} + +template +inline base_owned_ref::base_owned_ref( + base_owned_ref&& other) noexcept + : storage_(other.get()) { + internal::dbglog("New move from ref=%p other=%p", other.get(), &other); + internal::dbglog("New move to ref=%p this=%p", get(), this); + // JObject is a simple type and does not support move semantics so we explicitly + // clear other + other.set(nullptr); +} + +template +template +base_owned_ref::base_owned_ref(base_owned_ref&& other) noexcept + : storage_(other.get()) { + internal::dbglog("New move from ref=%p other=%p", other.get(), &other); + internal::dbglog("New move to ref=%p this=%p", get(), this); + // JObject is a simple type and does not support move semantics so we explicitly + // clear other + other.set(nullptr); +} + +template +inline base_owned_ref::~base_owned_ref() noexcept { + reset(); + internal::dbglog("Ref destruct ref=%p this=%p", get(), this); +} + +template +inline auto base_owned_ref::release() noexcept -> javaobject { + auto value = get(); + internal::dbglog("Ref release ref=%p this=%p", value, this); + set(nullptr); + return value; +} + +template +inline void base_owned_ref::reset() noexcept { + reset(nullptr); +} + +template +inline void base_owned_ref::reset(javaobject reference) noexcept { + if (get()) { + assert(Alloc{}.verifyReference(reference)); + Alloc{}.deleteReference(get()); + } + set(reference); +} + +template +inline auto base_owned_ref::get() const noexcept -> javaobject { + return storage_.jobj(); +} + +template +inline void base_owned_ref::set(javaobject ref) noexcept { + storage_.set(ref); +} + + +// weak_ref /////////////////////////////////////////////////////////////////////// + +template +inline weak_ref& weak_ref::operator=( + const weak_ref& other) { + auto otherCopy = other; + swap(*this, otherCopy); + return *this; +} + +template +inline weak_ref& weak_ref::operator=( + weak_ref&& other) noexcept { + internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p", + get(), this, other.get(), &other); + reset(other.release()); + return *this; +} + +template +local_ref weak_ref::lockLocal() const { + return adopt_local( + static_cast(LocalReferenceAllocator{}.newReference(get()))); +} + +template +global_ref weak_ref::lockGlobal() const { + return adopt_global( + static_cast(GlobalReferenceAllocator{}.newReference(get()))); +} + +template +inline void swap( + weak_ref& a, + weak_ref& b) noexcept { + internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p", + a.get(), &a, b.get(), &b); + a.storage_.swap(b.storage_); +} + + +// basic_strong_ref //////////////////////////////////////////////////////////////////////////// + +template +inline basic_strong_ref& basic_strong_ref::operator=( + const basic_strong_ref& other) { + auto otherCopy = other; + swap(*this, otherCopy); + return *this; +} + +template +inline basic_strong_ref& basic_strong_ref::operator=( + basic_strong_ref&& other) noexcept { + internal::dbglog("Op= move ref=%p this=%p oref=%p other=%p", + get(), this, other.get(), &other); + reset(other.release()); + return *this; +} + +template +inline alias_ref basic_strong_ref::releaseAlias() noexcept { + return wrap_alias(release()); +} + +template +inline basic_strong_ref::operator bool() const noexcept { + return get() != nullptr; +} + +template +inline auto basic_strong_ref::operator->() noexcept -> Repr* { + return &storage_.get(); +} + +template +inline auto basic_strong_ref::operator->() const noexcept -> const Repr* { + return &storage_.get(); +} + +template +inline auto basic_strong_ref::operator*() noexcept -> Repr& { + return storage_.get(); +} + +template +inline auto basic_strong_ref::operator*() const noexcept -> const Repr& { + return storage_.get(); +} + +template +inline void swap( + basic_strong_ref& a, + basic_strong_ref& b) noexcept { + internal::dbglog("Ref swap a.ref=%p a=%p b.ref=%p b=%p", + a.get(), &a, b.get(), &b); + using std::swap; + a.storage_.swap(b.storage_); +} + + +// alias_ref ////////////////////////////////////////////////////////////////////////////// + +template +inline alias_ref::alias_ref() noexcept + : storage_{nullptr} +{} + +template +inline alias_ref::alias_ref(std::nullptr_t) noexcept + : storage_{nullptr} +{} + +template +inline alias_ref::alias_ref(const alias_ref& other) noexcept + : storage_{other.get()} +{} + +template +inline alias_ref::alias_ref(javaobject ref) noexcept + : storage_(ref) { + assert( + LocalReferenceAllocator{}.verifyReference(ref) || + GlobalReferenceAllocator{}.verifyReference(ref)); +} + +template +template +inline alias_ref::alias_ref(alias_ref other) noexcept + : storage_{other.get()} +{} + +template +template +inline alias_ref::alias_ref(const basic_strong_ref& other) noexcept + : storage_{other.get()} +{} + +template +inline alias_ref& alias_ref::operator=(alias_ref other) noexcept { + swap(*this, other); + return *this; +} + +template +inline alias_ref::operator bool() const noexcept { + return get() != nullptr; +} + +template +inline auto facebook::jni::alias_ref::get() const noexcept -> javaobject { + return storage_.jobj(); +} + +template +inline auto alias_ref::operator->() noexcept -> Repr* { + return &(**this); +} + +template +inline auto alias_ref::operator->() const noexcept -> const Repr* { + return &(**this); +} + +template +inline auto alias_ref::operator*() noexcept -> Repr& { + return storage_.get(); +} + +template +inline auto alias_ref::operator*() const noexcept -> const Repr& { + return storage_.get(); +} + +template +inline void alias_ref::set(javaobject ref) noexcept { + storage_.set(ref); +} + +template +inline void swap(alias_ref& a, alias_ref& b) noexcept { + a.storage_.swap(b.storage_); +} + +// Could reduce code duplication by using a pointer-to-function +// template argument. I'm not sure whether that would make the code +// more maintainable (DRY), or less (too clever/confusing.). +template +enable_if_t(), local_ref> +static_ref_cast(const local_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return make_local(p); +} + +template +enable_if_t(), global_ref> +static_ref_cast(const global_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return make_global(p); +} + +template +enable_if_t(), alias_ref> +static_ref_cast(const alias_ref& ref) noexcept +{ + T p = static_cast(ref.get()); + return wrap_alias(p); +} + +template +auto dynamic_ref_cast(const RefType& ref) -> +enable_if_t(), decltype(static_ref_cast(ref))> +{ + if (! ref) { + return decltype(static_ref_cast(ref))(); + } + + std::string target_class_name{jtype_traits::base_name()}; + + // If not found, will throw an exception. + alias_ref target_class = findClassStatic(target_class_name.c_str()); + + local_ref source_class = ref->getClass(); + + if ( ! source_class->isAssignableFrom(target_class)) { + throwNewJavaException("java/lang/ClassCastException", + "Tried to cast from %s to %s.", + source_class->toString().c_str(), + target_class_name.c_str()); + } + + return static_ref_cast(ref); +} + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/References.h b/lib/fb/src/main/cpp/include/fb/fbjni/References.h new file mode 100644 index 00000000..b3484e5e --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/References.h @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + + +/** @file References.h + * + * Functionality similar to smart pointers, but for references into the VM. Four main reference + * types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic + * templates that and refer to objects in the jobject hierarchy. The type of the referred objects + * are specified using the template parameter. All reference types except alias_ref own their + * underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context + * of std smart pointers, these references behave like unique_ptr, and have basically the same + * interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying + * JNI reference (like the parameters passed directly to JNI functions), is released. The alias + * references provides no ownership and is a simple wrapper for plain JNI references. + * + * All but the weak references provides access to the underlying object using dereferencing, and a + * get() method. It is also possible to convert these references to booleans to test for nullity. + * To access the underlying object of a weak reference, the reference must either be released, or + * the weak reference can be used to create a local or global reference. + * + * An owning reference is created either by moving the reference from an existing owned reference, + * by copying an existing owned reference (which creates a new underlying reference), by using the + * default constructor which initialize the reference to nullptr, or by using a helper function. The + * helper function exist in two flavors: make_XXX or adopt_XXX. + * + * Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the + * plain JNI reference so be sure that no one else owns the reference when you adopt it, and make + * sure that you know what kind of reference it is. + * + * New owned references can be created from existing plain JNI references, alias references, local + * references, and global references (i.e. non-weak references) using the make_local, make_global, + * and make_weak functions. + * + * Alias references can be implicitly initialized using global, local and plain JNI references using + * the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather + * create a separate reference that we do own, leaving the passed-in reference to its fate. + * + * Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart + * reference of the same type. In the case of copy assignment a new reference is created. Alias + * reference can also be assigned new values, but since they are simple wrappers of plain JNI + * references there is no move semantics involved. + * + * Alias references are special in that they do not own the object and can therefore safely be + * converted to and from its corresponding plain JNI reference. They are useful as parameters of + * functions that do not affect the lifetime of a reference. Usage can be compared with using plain + * JNI pointers as parameters where a function does not take ownership of the underlying object. + * + * The local, global, and alias references makes it possible to access methods in the underlying + * objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are + * supported (see example below). The wrappers also supports inheritance so a wrapper can inherit + * from another wrapper to gain access to its functionality. As an example the jstring wrapper + * inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for + * example call the toString() method using the jclass wrapper, or any other class that inherits + * from the jobject wrapper. + * + * Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if + * you have a jobject that refers to a Java String you will need to cast it to jstring to get the + * jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one + * stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast + * wisely. + * + * @include WrapperSample.cpp + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +#include "ReferenceAllocators.h" +#include "TypeTraits.h" +#include "References-forward.h" + +namespace facebook { +namespace jni { + +/// Convenience function to wrap an existing local reference +template +local_ref adopt_local(T ref) noexcept; + +/// Convenience function to wrap an existing global reference +template +global_ref adopt_global(T ref) noexcept; + +/// Convenience function to wrap an existing weak reference +template +weak_ref adopt_weak_global(T ref) noexcept; + + +/// Swaps two owning references of the same type +template +void swap(weak_ref& a, weak_ref& b) noexcept; + +/// Swaps two owning references of the same type +template +void swap(basic_strong_ref& a, basic_strong_ref& b) noexcept; + +/** + * Retrieve the plain reference from a plain reference. + */ +template +enable_if_t(), T> getPlainJniReference(T ref); + +/** + * Retrieve the plain reference from an alias reference. + */ +template +JniType getPlainJniReference(alias_ref ref); + +/** + * Retrieve the plain JNI reference from any reference owned reference. + */ +template +JniType getPlainJniReference(const base_owned_ref& ref); + +class JObject; +class JClass; + +namespace detail { + +template +struct HasJniRefRepr : std::false_type {}; + +template +struct HasJniRefRepr::value, void>::type> : std::true_type { + using type = typename T::JniRefRepr; +}; + +template +struct RefReprType { + using type = typename std::conditional::value, typename HasJniRefRepr::type, JObjectWrapper>::type; + static_assert(std::is_base_of::value, + "Repr type missing JObject base."); + static_assert(std::is_same::type>::value, + "RefReprType not idempotent"); +}; + +template +struct RefReprType::value, void>::type> { + using type = T; + static_assert(std::is_base_of::value, + "Repr type missing JObject base."); + static_assert(std::is_same::type>::value, + "RefReprType not idempotent"); +}; + +template +struct JavaObjectType { + using type = typename RefReprType::type::javaobject; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct JavaObjectType> { + using type = T; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct JavaObjectType { + using type = T*; + static_assert(IsPlainJniReference(), + "JavaObjectType not a plain jni reference"); + static_assert(std::is_same::type>::value, + "JavaObjectType not idempotent"); +}; + +template +struct ReprStorage { + explicit ReprStorage(JniType obj) noexcept; + + void set(JniType obj) noexcept; + + Repr& get() noexcept; + const Repr& get() const noexcept; + JniType jobj() const noexcept; + + void swap(ReprStorage& other) noexcept; + private: + ReprStorage() = delete; + ReprStorage(const ReprStorage&) = delete; + ReprStorage(ReprStorage&&) = delete; + ReprStorage& operator=(const ReprStorage&) = delete; + ReprStorage& operator=(ReprStorage&&) = delete; + + using Storage = typename std::aligned_storage::type; + Storage storage_; +}; + +} // namespace detail + +/** + * Create a new local reference from an existing reference + * + * @param ref a plain JNI, alias, or strong reference + * @return an owned local reference (referring to null if the input does) + * @throws std::bad_alloc if the JNI reference could not be created + */ +template +enable_if_t(), local_ref>> +make_local(const T& r); + +/** + * Create a new global reference from an existing reference + * + * @param ref a plain JNI, alias, or strong reference + * @return an owned global reference (referring to null if the input does) + * @throws std::bad_alloc if the JNI reference could not be created + */ +template +enable_if_t(), global_ref>> +make_global(const T& r); + +/** + * Create a new weak global reference from an existing reference + * + * @param ref a plain JNI, alias, or strong reference + * @return an owned weak global reference (referring to null if the input does) + * @throws std::bad_alloc if the returned reference is null + */ +template +enable_if_t(), weak_ref>> +make_weak(const T& r); + +/** + * Compare two references to see if they refer to the same object + */ +template +enable_if_t() && IsNonWeakReference(), bool> +operator==(const T1& a, const T2& b); + +/** + * Compare two references to see if they don't refer to the same object + */ +template +enable_if_t() && IsNonWeakReference(), bool> +operator!=(const T1& a, const T2& b); + +template +class base_owned_ref { + public: + using javaobject = JniType; + + /** + * Release the ownership and set the reference to null. Thus no deleter is invoked. + * @return Returns the reference + */ + javaobject release() noexcept; + + /** + * Reset the reference to refer to nullptr. + */ + void reset() noexcept; + + protected: + using Repr = ReprType; + detail::ReprStorage storage_; + + javaobject get() const noexcept; + void set(javaobject ref) noexcept; + + /* + * Wrap an existing reference and transfers its ownership to the newly created unique reference. + * NB! Does not create a new reference + */ + explicit base_owned_ref(javaobject reference) noexcept; + + /// Create a null reference + base_owned_ref() noexcept; + + /// Create a null reference + explicit base_owned_ref(std::nullptr_t) noexcept; + + /// Copy constructor (note creates a new reference) + base_owned_ref(const base_owned_ref& other); + template + base_owned_ref(const base_owned_ref& other); + + /// Transfers ownership of an underlying reference from one unique reference to another + base_owned_ref(base_owned_ref&& other) noexcept; + template + base_owned_ref(base_owned_ref&& other) noexcept; + + /// The delete the underlying reference if applicable + ~base_owned_ref() noexcept; + + + /// Assignment operator (note creates a new reference) + base_owned_ref& operator=(const base_owned_ref& other); + + /// Assignment by moving a reference thus not creating a new reference + base_owned_ref& operator=(base_owned_ref&& rhs) noexcept; + + void reset(javaobject reference) noexcept; + + friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref); + + template + friend class base_owned_ref; +}; + + +/** + * A smart reference that owns its underlying JNI reference. The class provides basic + * functionality to handle a reference but gives no access to it unless the reference is + * released, thus no longer owned. The API is stolen with pride from unique_ptr and the + * semantics should be basically the same. This class should not be used directly, instead use + * @ref weak_ref + */ +template +class weak_ref : public base_owned_ref { + public: + using javaobject = JniType; + + using Allocator = WeakGlobalReferenceAllocator; + + // This inherits non-default, non-copy, non-move ctors. + using base_owned_ref::base_owned_ref; + + /// Create a null reference + weak_ref() noexcept + : base_owned_ref{} {} + + /// Create a null reference + explicit weak_ref(std::nullptr_t) noexcept + : base_owned_ref{nullptr} {} + + /// Copy constructor (note creates a new reference) + weak_ref(const weak_ref& other) + : base_owned_ref{other} {} + + // This needs to be explicit to change its visibility. + template + weak_ref(const weak_ref& other) + : base_owned_ref{other} {} + + /// Transfers ownership of an underlying reference from one unique reference to another + weak_ref(weak_ref&& other) noexcept + : base_owned_ref{std::move(other)} {} + + + /// Assignment operator (note creates a new reference) + weak_ref& operator=(const weak_ref& other); + + /// Assignment by moving a reference thus not creating a new reference + weak_ref& operator=(weak_ref&& rhs) noexcept; + + // Creates an owned local reference to the referred object or to null if the object is reclaimed + local_ref lockLocal() const; + + // Creates an owned global reference to the referred object or to null if the object is reclaimed + global_ref lockGlobal() const; + + private: + // get/release/reset on weak_ref are not exposed to users. + using base_owned_ref::get; + using base_owned_ref::release; + using base_owned_ref::reset; + /* + * Wrap an existing reference and transfers its ownership to the newly created unique reference. + * NB! Does not create a new reference + */ + explicit weak_ref(javaobject reference) noexcept + : base_owned_ref{reference} {} + + template friend class weak_ref; + friend weak_ref adopt_weak_global(javaobject ref) noexcept; + friend void swap(weak_ref& a, weak_ref& b) noexcept; +}; + + +/** + * A class representing owned strong references to Java objects. This class + * should not be used directly, instead use @ref local_ref, or @ref global_ref. + */ +template +class basic_strong_ref : public base_owned_ref { + using typename base_owned_ref::Repr; + public: + using javaobject = JniType; + + using Allocator = Alloc; + + // This inherits non-default, non-copy, non-move ctors. + using base_owned_ref::base_owned_ref; + using base_owned_ref::release; + using base_owned_ref::reset; + + /// Create a null reference + basic_strong_ref() noexcept + : base_owned_ref{} {} + + /// Create a null reference + explicit basic_strong_ref(std::nullptr_t) noexcept + : base_owned_ref{nullptr} {} + + /// Copy constructor (note creates a new reference) + basic_strong_ref(const basic_strong_ref& other) + : base_owned_ref{other} {} + + // This needs to be explicit to change its visibility. + template + basic_strong_ref(const basic_strong_ref& other) + : base_owned_ref{other} {} + + /// Transfers ownership of an underlying reference from one unique reference to another + basic_strong_ref(basic_strong_ref&& other) noexcept + : base_owned_ref{std::move(other)} {} + + /// Assignment operator (note creates a new reference) + basic_strong_ref& operator=(const basic_strong_ref& other); + + /// Assignment by moving a reference thus not creating a new reference + basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept; + + /// Get the plain JNI reference + using base_owned_ref::get; + + /// Release the ownership of the reference and return the wrapped reference in an alias + alias_ref releaseAlias() noexcept; + + /// Checks if the reference points to a non-null object + explicit operator bool() const noexcept; + + /// Access the functionality provided by the object wrappers + Repr* operator->() noexcept; + + /// Access the functionality provided by the object wrappers + const Repr* operator->() const noexcept; + + /// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking) + Repr& operator*() noexcept; + + /// Provide a const reference to the underlying wrapper (be sure that it is non-null + /// before invoking) + const Repr& operator*() const noexcept; + + private: + + using base_owned_ref::storage_; + + /* + * Wrap an existing reference and transfers its ownership to the newly created unique reference. + * NB! Does not create a new reference + */ + explicit basic_strong_ref(javaobject reference) noexcept + : base_owned_ref{reference} {} + + + friend local_ref adopt_local(T ref) noexcept; + friend global_ref adopt_global(T ref) noexcept; + friend void swap(basic_strong_ref& a, basic_strong_ref& b) noexcept; +}; + + +template +enable_if_t(), alias_ref> wrap_alias(T ref) noexcept; + +/// Swaps to alias reference of the same type +template +void swap(alias_ref& a, alias_ref& b) noexcept; + +/** + * A non-owning variant of the smart references (a dumb reference). These references still provide + * access to the functionality of the @ref JObjectWrapper specializations including exception + * handling and ease of use. Use this representation when you don't want to claim ownership of the + * underlying reference (compare to using raw pointers instead of smart pointers.) For symmetry use + * @ref alias_ref instead of this class. + */ +template +class alias_ref { + using Repr = ReprType; + + public: + using javaobject = JniType; + + /// Create a null reference + alias_ref() noexcept; + + /// Create a null reference + alias_ref(std::nullptr_t) noexcept; + + /// Copy constructor + alias_ref(const alias_ref& other) noexcept; + + /// Wrap an existing plain JNI reference + /* implicit */ alias_ref(javaobject ref) noexcept; + + /// Wrap an existing smart reference of any type convertible to T + template< + typename TOther, + typename = enable_if_t< + IsConvertible, javaobject>(), T> + > + alias_ref(alias_ref other) noexcept; + + /// Wrap an existing alias reference of a type convertible to T + template< + typename TOther, + typename AOther, + typename = enable_if_t< + IsConvertible, javaobject>(), T> + > + alias_ref(const basic_strong_ref& other) noexcept; + + /// Assignment operator + alias_ref& operator=(alias_ref other) noexcept; + + /// Checks if the reference points to a non-null object + explicit operator bool() const noexcept; + + /// Converts back to a plain JNI reference + javaobject get() const noexcept; + + /// Access the functionality provided by the object wrappers + Repr* operator->() noexcept; + + /// Access the functionality provided by the object wrappers + const Repr* operator->() const noexcept; + + /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking) + Repr& operator*() noexcept; + + /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking) + const Repr& operator*() const noexcept; + + private: + void set(javaobject ref) noexcept; + + detail::ReprStorage storage_; + + friend void swap(alias_ref& a, alias_ref& b) noexcept; +}; + + +/** + * RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame. + * + * This is useful when you have a call which is initiated from C++-land, and therefore + * doesn't automatically get a local JNI frame managed for you by the JNI framework. + */ +class FBEXPORT JniLocalScope { +public: + JniLocalScope(JNIEnv* p_env, jint capacity); + ~JniLocalScope(); + +private: + JNIEnv* env_; + bool hasFrame_; +}; + +template +enable_if_t(), local_ref> +static_ref_cast(const local_ref& ref) noexcept; + +template +enable_if_t(), global_ref> +static_ref_cast(const global_ref& ref) noexcept; + +template +enable_if_t(), alias_ref> +static_ref_cast(const alias_ref& ref) noexcept; + +template +auto dynamic_ref_cast(const RefType& ref) -> +enable_if_t(), decltype(static_ref_cast(ref))> ; + +}} + +#include "References-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h b/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h new file mode 100644 index 00000000..e2817473 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Registration-inl.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "Exceptions.h" +#include "Hybrid.h" + +namespace facebook { +namespace jni { + +namespace detail { + +#ifdef __i386__ +// X86 ABI forces 16 byte stack allignment on calls. Unfortunately +// sometimes Dalvik chooses not to obey the ABI: +// - https://code.google.com/p/android/issues/detail?id=61012 +// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/ +// Therefore, we tell the compiler to re-align the stack on entry +// to our JNI functions. +#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer)) +#else +#define JNI_ENTRY_POINT +#endif + +// registration wrapper for legacy JNI-style functions + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(JNIEnv*, C, Args... args)) { + struct funcWrapper { + JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, Args... args) { + // Note that if func was declared noexcept, then both gcc and clang are smart + // enough to elide the try/catch. + try { + (*func)(env, static_cast(obj), args...); + } catch (...) { + translatePendingCppExceptionToJavaException(); + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) { + struct funcWrapper { + JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) { + try { + return (*func)(env, static_cast>(obj), args...); + } catch (...) { + translatePendingCppExceptionToJavaException(); + return R{}; + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +// registration wrappers for functions, with autoconversion of arguments. + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(void (*)(alias_ref, Args... args)) { + struct funcWrapper { + JNI_ENTRY_POINT static void call(JNIEnv*, jobject obj, + typename Convert::type>::jniType... args) { + try { + (*func)(static_cast>(obj), Convert::type>::fromJni(args)...); + } catch (...) { + translatePendingCppExceptionToJavaException(); + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref, Args... args)) { + struct funcWrapper { + + JNI_ENTRY_POINT static typename Convert::type>::jniType call(JNIEnv*, jobject obj, + typename Convert::type>::jniType... args) { + try { + return Convert::type>::toJniRet( + (*func)(static_cast>(obj), Convert::type>::fromJni(args)...)); + } catch (...) { + using jniRet = typename Convert::type>::jniType; + translatePendingCppExceptionToJavaException(); + return jniRet{}; + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +// registration wrappers for non-static methods, with autoconvertion of arguments. + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)) { + struct funcWrapper { + JNI_ENTRY_POINT static void call(JNIEnv* env, jobject obj, + typename Convert::type>::jniType... args) { + try { + try { + auto aref = wrap_alias(static_cast(obj)); + // This is usually a noop, but if the hybrid object is a + // base class of other classes which register JNI methods, + // this will get the right type for the registered method. + auto cobj = static_cast(facebook::jni::cthis(aref)); + (cobj->*method)(Convert::type>::fromJni(args)...); + } catch (const std::exception& ex) { + C::mapException(ex); + throw; + } + } catch (...) { + translatePendingCppExceptionToJavaException(); + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +template +inline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) { + struct funcWrapper { + + JNI_ENTRY_POINT static typename Convert::type>::jniType call(JNIEnv* env, jobject obj, + typename Convert::type>::jniType... args) { + try { + try { + auto aref = wrap_alias(static_cast(obj)); + // This is usually a noop, but if the hybrid object is a + // base class of other classes which register JNI methods, + // this will get the right type for the registered method. + auto cobj = static_cast(facebook::jni::cthis(aref)); + return Convert::type>::toJniRet( + (cobj->*method)(Convert::type>::fromJni(args)...)); + } catch (const std::exception& ex) { + C::mapException(ex); + throw; + } + } catch (...) { + using jniRet = typename Convert::type>::jniType; + translatePendingCppExceptionToJavaException(); + return jniRet{}; + } + } + }; + + // This intentionally erases the real type; JNI will do it anyway + return reinterpret_cast(&(funcWrapper::call)); +} + +template +inline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) { + return jmethod_traits::descriptor(); +} + +template +inline std::string makeDescriptor(R (*)(alias_ref, Args... args)) { + return jmethod_traits_from_cxx::descriptor(); +} + +template +inline std::string makeDescriptor(R (C::*)(Args... args)) { + return jmethod_traits_from_cxx::descriptor(); +} + +} + +}} diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h b/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h new file mode 100644 index 00000000..654fdab2 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/Registration.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include "References.h" + +namespace facebook { +namespace jni { + +namespace detail { + +// This uses the real JNI function as a non-type template parameter to +// cause a (static member) function to exist with the same signature, +// but with try/catch exception translation. +template +NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(JNIEnv*, jobject, Args... args)); + +// Same as above, but for non-void return types. +template +NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args)); + +// Automatically wrap object argument, and don't take env explicitly. +template +NativeMethodWrapper* exceptionWrapJNIMethod(void (*func0)(alias_ref, Args... args)); + +// Automatically wrap object argument, and don't take env explicitly, +// non-void return type. +template +NativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref, Args... args)); + +// Extract C++ instance from object, and invoke given method on it. +template +NativeMethodWrapper* exceptionWrapJNIMethod(void (C::*method0)(Args... args)); + +// Extract C++ instance from object, and invoke given method on it, +// non-void return type +template +NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)); + +// This uses deduction to figure out the descriptor name if the types +// are primitive or have JObjectWrapper specializations. +template +std::string makeDescriptor(R (*func)(JNIEnv*, C, Args... args)); + +// This uses deduction to figure out the descriptor name if the types +// are primitive or have JObjectWrapper specializations. +template +std::string makeDescriptor(R (*func)(alias_ref, Args... args)); + +// This uses deduction to figure out the descriptor name if the types +// are primitive or have JObjectWrapper specializations. +template +std::string makeDescriptor(R (C::*method0)(Args... args)); + +} + +// We have to use macros here, because the func needs to be used +// as both a decltype expression argument and as a non-type template +// parameter, since C++ provides no way for translateException +// to deduce the type of its non-type template parameter. +// The empty string in the macros below ensures that name +// is always a string literal (because that syntax is only +// valid when name is a string literal). +#define makeNativeMethod2(name, func) \ + { name "", ::facebook::jni::detail::makeDescriptor(&func), \ + ::facebook::jni::detail::exceptionWrapJNIMethod(&func) } + +#define makeNativeMethod3(name, desc, func) \ + { name "", desc, \ + ::facebook::jni::detail::exceptionWrapJNIMethod(&func) } + +// Variadic template hacks to get macros with different numbers of +// arguments. Usage instructions are in CoreClasses.h. +#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count +#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__) + +}} + +#include "Registration-inl.h" diff --git a/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h b/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h new file mode 100644 index 00000000..36c8bec1 --- /dev/null +++ b/lib/fb/src/main/cpp/include/fb/fbjni/TypeTraits.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +#include "References-forward.h" + +namespace facebook { +namespace jni { + +/// Generic std::enable_if helper +template +using enable_if_t = typename std::enable_if::type; + +/// Generic std::is_convertible helper +template +constexpr bool IsConvertible() { + return std::is_convertible::value; +} + +template class TT, typename T> +struct is_instantiation_of : std::false_type {}; + +template class TT, typename... Ts> +struct is_instantiation_of> : std::true_type {}; + +template class TT, typename... Ts> +constexpr bool IsInstantiationOf() { + return is_instantiation_of::value; +} + +/// Metafunction to determine whether a type is a JNI reference or not +template +struct is_plain_jni_reference : + std::integral_constant::value && + std::is_base_of< + typename std::remove_pointer::type, + typename std::remove_pointer::type>::value> {}; + +/// Helper to simplify use of is_plain_jni_reference +template +constexpr bool IsPlainJniReference() { + return is_plain_jni_reference::value; +} + +/// Metafunction to determine whether a type is a primitive JNI type or not +template +struct is_jni_primitive : + std::integral_constant::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value> {}; + +/// Helper to simplify use of is_jni_primitive +template +constexpr bool IsJniPrimitive() { + return is_jni_primitive::value; +} + +/// Metafunction to determine whether a type is a JNI array of primitives or not +template +struct is_jni_primitive_array : + std::integral_constant::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value> {}; + +/// Helper to simplify use of is_jni_primitive_array +template +constexpr bool IsJniPrimitiveArray() { + return is_jni_primitive_array::value; +} + +/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type +template +struct is_jni_scalar : + std::integral_constant::value || + is_jni_primitive::value> {}; + +/// Helper to simplify use of is_jni_scalar +template +constexpr bool IsJniScalar() { + return is_jni_scalar::value; +} + +// Metafunction to determine if a type is a JNI type +template +struct is_jni_type : + std::integral_constant::value || + std::is_void::value> {}; + +/// Helper to simplify use of is_jni_type +template +constexpr bool IsJniType() { + return is_jni_type::value; +} + +template +struct is_non_weak_reference : + std::integral_constant() || + IsInstantiationOf() || + IsInstantiationOf()> {}; + +template +constexpr bool IsNonWeakReference() { + return is_non_weak_reference::value; +} + +template +struct is_any_reference : + std::integral_constant() || + IsInstantiationOf() || + IsInstantiationOf() || + IsInstantiationOf()> {}; + +template +constexpr bool IsAnyReference() { + return is_any_reference::value; +} + +template +struct reference_traits { + using plain_jni_reference_t = JniType; + static_assert(IsPlainJniReference(), "Need a plain JNI reference"); +}; + +template