diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6809f3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +Packages/ +Package.pins +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +Carthage/Checkouts +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..4a07357 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,6 @@ +identifier_name: + excluded: # excluded via string array + - id + - uid + - x + - y diff --git a/.travis,yml b/.travis,yml new file mode 100644 index 0000000..9e01e2e --- /dev/null +++ b/.travis,yml @@ -0,0 +1,7 @@ +language: swift +osx_image: xcode11 +before_install: +script: + - xcodebuild clean -scheme FlexLayout | xcpretty + + - xcodebuild -project FlexLayout.xcodeproj -scheme FlexLayout -destination 'name=Generic iOS Device' | xcpretty \ No newline at end of file diff --git a/FlexLayout.xcodeproj/project.pbxproj b/FlexLayout.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e544ac7 --- /dev/null +++ b/FlexLayout.xcodeproj/project.pbxproj @@ -0,0 +1,558 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + CE4DFABE22DC9A25002162E6 /* NSValue+YGValue.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFA9B22DC9A25002162E6 /* NSValue+YGValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFABF22DC9A25002162E6 /* CSS+Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFA9C22DC9A25002162E6 /* CSS+Value.swift */; }; + CE4DFAC022DC9A25002162E6 /* CSS+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFA9D22DC9A25002162E6 /* CSS+Types.swift */; }; + CE4DFAC122DC9A25002162E6 /* YGFloatOptional.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFA9F22DC9A25002162E6 /* YGFloatOptional.h */; }; + CE4DFAC222DC9A25002162E6 /* Utils.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA022DC9A25002162E6 /* Utils.h */; }; + CE4DFAC322DC9A25002162E6 /* CompactValue.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA122DC9A25002162E6 /* CompactValue.h */; }; + CE4DFAC422DC9A25002162E6 /* Yoga-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA222DC9A25002162E6 /* Yoga-internal.h */; }; + CE4DFAC522DC9A25002162E6 /* YGLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA322DC9A25002162E6 /* YGLayout.h */; }; + CE4DFAC622DC9A25002162E6 /* log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAA422DC9A25002162E6 /* log.cpp */; }; + CE4DFAC722DC9A25002162E6 /* Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAA522DC9A25002162E6 /* Utils.cpp */; }; + CE4DFAC822DC9A25002162E6 /* YGNodePrint.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA622DC9A25002162E6 /* YGNodePrint.h */; }; + CE4DFAC922DC9A25002162E6 /* YGEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA722DC9A25002162E6 /* YGEnums.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFACA22DC9A25002162E6 /* YGLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAA822DC9A25002162E6 /* YGLayout.cpp */; }; + CE4DFACB22DC9A25002162E6 /* YGNode.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAA922DC9A25002162E6 /* YGNode.h */; }; + CE4DFACC22DC9A25002162E6 /* YGMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAAA22DC9A25002162E6 /* YGMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFACD22DC9A25002162E6 /* Yoga.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAAB22DC9A25002162E6 /* Yoga.cpp */; }; + CE4DFACE22DC9A25002162E6 /* YGStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAAC22DC9A25002162E6 /* YGStyle.cpp */; }; + CE4DFACF22DC9A25002162E6 /* YGMarker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAAD22DC9A25002162E6 /* YGMarker.cpp */; }; + CE4DFAD022DC9A25002162E6 /* Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAAE22DC9A25002162E6 /* Yoga.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFAD122DC9A25002162E6 /* YGNodePrint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAAF22DC9A25002162E6 /* YGNodePrint.cpp */; }; + CE4DFAD222DC9A25002162E6 /* YGStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB022DC9A25002162E6 /* YGStyle.h */; }; + CE4DFAD322DC9A25002162E6 /* YGEnums.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAB122DC9A25002162E6 /* YGEnums.cpp */; }; + CE4DFAD422DC9A25002162E6 /* log.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB222DC9A25002162E6 /* log.h */; }; + CE4DFAD522DC9A25002162E6 /* YGValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAB322DC9A25002162E6 /* YGValue.cpp */; }; + CE4DFAD622DC9A25002162E6 /* YGConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB422DC9A25002162E6 /* YGConfig.h */; }; + CE4DFAD722DC9A25002162E6 /* instrumentation.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB522DC9A25002162E6 /* instrumentation.h */; }; + CE4DFAD822DC9A25002162E6 /* YGMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB622DC9A25002162E6 /* YGMarker.h */; }; + CE4DFAD922DC9A25002162E6 /* YGConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAB722DC9A25002162E6 /* YGConfig.cpp */; }; + CE4DFADA22DC9A25002162E6 /* YGNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAB822DC9A25002162E6 /* YGNode.cpp */; }; + CE4DFADB22DC9A25002162E6 /* YGValue.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAB922DC9A25002162E6 /* YGValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFADC22DC9A25002162E6 /* Header.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFABA22DC9A25002162E6 /* Header.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE4DFADD22DC9A25002162E6 /* CSS.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFABB22DC9A25002162E6 /* CSS.swift */; }; + CE4DFADE22DC9A25002162E6 /* NSValue+YGValue.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFABC22DC9A25002162E6 /* NSValue+YGValue.m */; }; + CE4DFADF22DC9A25002162E6 /* FlexLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFABD22DC9A25002162E6 /* FlexLayout.swift */; }; + CE4DFAE222DC9B8F002162E6 /* YGLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = CE4DFAE022DC9B8F002162E6 /* YGLayout.m */; }; + CE4DFAE322DC9B8F002162E6 /* YGLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CE4DFAE122DC9B8F002162E6 /* YGLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CEF0E32822DC57570000C940 /* FlexLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CEF0E31A22DC57560000C940 /* FlexLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CEF0E39522DC59660000C940 /* Extension in Frameworks */ = {isa = PBXBuildFile; productRef = CEF0E39422DC59660000C940 /* Extension */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE4DFA9B22DC9A25002162E6 /* NSValue+YGValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+YGValue.h"; sourceTree = ""; }; + CE4DFA9C22DC9A25002162E6 /* CSS+Value.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSS+Value.swift"; sourceTree = ""; }; + CE4DFA9D22DC9A25002162E6 /* CSS+Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSS+Types.swift"; sourceTree = ""; }; + CE4DFA9F22DC9A25002162E6 /* YGFloatOptional.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGFloatOptional.h; sourceTree = ""; }; + CE4DFAA022DC9A25002162E6 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utils.h; sourceTree = ""; }; + CE4DFAA122DC9A25002162E6 /* CompactValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompactValue.h; sourceTree = ""; }; + CE4DFAA222DC9A25002162E6 /* Yoga-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Yoga-internal.h"; sourceTree = ""; }; + CE4DFAA322DC9A25002162E6 /* YGLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGLayout.h; sourceTree = ""; }; + CE4DFAA422DC9A25002162E6 /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; }; + CE4DFAA522DC9A25002162E6 /* Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Utils.cpp; sourceTree = ""; }; + CE4DFAA622DC9A25002162E6 /* YGNodePrint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNodePrint.h; sourceTree = ""; }; + CE4DFAA722DC9A25002162E6 /* YGEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGEnums.h; sourceTree = ""; }; + CE4DFAA822DC9A25002162E6 /* YGLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGLayout.cpp; sourceTree = ""; }; + CE4DFAA922DC9A25002162E6 /* YGNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGNode.h; sourceTree = ""; }; + CE4DFAAA22DC9A25002162E6 /* YGMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGMacros.h; sourceTree = ""; }; + CE4DFAAB22DC9A25002162E6 /* Yoga.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Yoga.cpp; sourceTree = ""; }; + CE4DFAAC22DC9A25002162E6 /* YGStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGStyle.cpp; sourceTree = ""; }; + CE4DFAAD22DC9A25002162E6 /* YGMarker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGMarker.cpp; sourceTree = ""; }; + CE4DFAAE22DC9A25002162E6 /* Yoga.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Yoga.h; sourceTree = ""; }; + CE4DFAAF22DC9A25002162E6 /* YGNodePrint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGNodePrint.cpp; sourceTree = ""; }; + CE4DFAB022DC9A25002162E6 /* YGStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGStyle.h; sourceTree = ""; }; + CE4DFAB122DC9A25002162E6 /* YGEnums.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGEnums.cpp; sourceTree = ""; }; + CE4DFAB222DC9A25002162E6 /* log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = ""; }; + CE4DFAB322DC9A25002162E6 /* YGValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGValue.cpp; sourceTree = ""; }; + CE4DFAB422DC9A25002162E6 /* YGConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGConfig.h; sourceTree = ""; }; + CE4DFAB522DC9A25002162E6 /* instrumentation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = instrumentation.h; sourceTree = ""; }; + CE4DFAB622DC9A25002162E6 /* YGMarker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGMarker.h; sourceTree = ""; }; + CE4DFAB722DC9A25002162E6 /* YGConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGConfig.cpp; sourceTree = ""; }; + CE4DFAB822DC9A25002162E6 /* YGNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = YGNode.cpp; sourceTree = ""; }; + CE4DFAB922DC9A25002162E6 /* YGValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGValue.h; sourceTree = ""; }; + CE4DFABA22DC9A25002162E6 /* Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Header.h; sourceTree = ""; }; + CE4DFABB22DC9A25002162E6 /* CSS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSS.swift; sourceTree = ""; }; + CE4DFABC22DC9A25002162E6 /* NSValue+YGValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+YGValue.m"; sourceTree = ""; }; + CE4DFABD22DC9A25002162E6 /* FlexLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlexLayout.swift; sourceTree = ""; }; + CE4DFAE022DC9B8F002162E6 /* YGLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YGLayout.m; sourceTree = ""; }; + CE4DFAE122DC9B8F002162E6 /* YGLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YGLayout.h; sourceTree = ""; }; + CEF0E31722DC57560000C940 /* FlexLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FlexLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CEF0E31A22DC57560000C940 /* FlexLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FlexLayout.h; sourceTree = ""; }; + CEF0E31B22DC57560000C940 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CEF0E31422DC57560000C940 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CEF0E39522DC59660000C940 /* Extension in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE4DFA9A22DC9A25002162E6 /* Sources */ = { + isa = PBXGroup; + children = ( + CE4DFABB22DC9A25002162E6 /* CSS.swift */, + CE4DFA9D22DC9A25002162E6 /* CSS+Types.swift */, + CE4DFA9C22DC9A25002162E6 /* CSS+Value.swift */, + CE4DFABD22DC9A25002162E6 /* FlexLayout.swift */, + CE4DFABA22DC9A25002162E6 /* Header.h */, + CE4DFA9B22DC9A25002162E6 /* NSValue+YGValue.h */, + CE4DFABC22DC9A25002162E6 /* NSValue+YGValue.m */, + CE4DFAE122DC9B8F002162E6 /* YGLayout.h */, + CE4DFAE022DC9B8F002162E6 /* YGLayout.m */, + CE4DFA9E22DC9A25002162E6 /* yoga */, + ); + path = Sources; + sourceTree = ""; + }; + CE4DFA9E22DC9A25002162E6 /* yoga */ = { + isa = PBXGroup; + children = ( + CE4DFA9F22DC9A25002162E6 /* YGFloatOptional.h */, + CE4DFAA022DC9A25002162E6 /* Utils.h */, + CE4DFAA122DC9A25002162E6 /* CompactValue.h */, + CE4DFAA222DC9A25002162E6 /* Yoga-internal.h */, + CE4DFAA322DC9A25002162E6 /* YGLayout.h */, + CE4DFAA422DC9A25002162E6 /* log.cpp */, + CE4DFAA522DC9A25002162E6 /* Utils.cpp */, + CE4DFAA622DC9A25002162E6 /* YGNodePrint.h */, + CE4DFAA722DC9A25002162E6 /* YGEnums.h */, + CE4DFAA822DC9A25002162E6 /* YGLayout.cpp */, + CE4DFAA922DC9A25002162E6 /* YGNode.h */, + CE4DFAAA22DC9A25002162E6 /* YGMacros.h */, + CE4DFAAB22DC9A25002162E6 /* Yoga.cpp */, + CE4DFAAC22DC9A25002162E6 /* YGStyle.cpp */, + CE4DFAAD22DC9A25002162E6 /* YGMarker.cpp */, + CE4DFAAE22DC9A25002162E6 /* Yoga.h */, + CE4DFAAF22DC9A25002162E6 /* YGNodePrint.cpp */, + CE4DFAB022DC9A25002162E6 /* YGStyle.h */, + CE4DFAB122DC9A25002162E6 /* YGEnums.cpp */, + CE4DFAB222DC9A25002162E6 /* log.h */, + CE4DFAB322DC9A25002162E6 /* YGValue.cpp */, + CE4DFAB422DC9A25002162E6 /* YGConfig.h */, + CE4DFAB522DC9A25002162E6 /* instrumentation.h */, + CE4DFAB622DC9A25002162E6 /* YGMarker.h */, + CE4DFAB722DC9A25002162E6 /* YGConfig.cpp */, + CE4DFAB822DC9A25002162E6 /* YGNode.cpp */, + CE4DFAB922DC9A25002162E6 /* YGValue.h */, + ); + path = yoga; + sourceTree = ""; + }; + CEF0E30D22DC57560000C940 = { + isa = PBXGroup; + children = ( + CE4DFA9A22DC9A25002162E6 /* Sources */, + CEF0E31922DC57560000C940 /* FlexLayout */, + CEF0E31822DC57560000C940 /* Products */, + ); + sourceTree = ""; + }; + CEF0E31822DC57560000C940 /* Products */ = { + isa = PBXGroup; + children = ( + CEF0E31722DC57560000C940 /* FlexLayout.framework */, + ); + name = Products; + sourceTree = ""; + }; + CEF0E31922DC57560000C940 /* FlexLayout */ = { + isa = PBXGroup; + children = ( + CEF0E31A22DC57560000C940 /* FlexLayout.h */, + CEF0E31B22DC57560000C940 /* Info.plist */, + ); + path = FlexLayout; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CEF0E31222DC57560000C940 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CEF0E32822DC57570000C940 /* FlexLayout.h in Headers */, + CE4DFAE322DC9B8F002162E6 /* YGLayout.h in Headers */, + CE4DFADC22DC9A25002162E6 /* Header.h in Headers */, + CE4DFABE22DC9A25002162E6 /* NSValue+YGValue.h in Headers */, + CE4DFACC22DC9A25002162E6 /* YGMacros.h in Headers */, + CE4DFADB22DC9A25002162E6 /* YGValue.h in Headers */, + CE4DFAD022DC9A25002162E6 /* Yoga.h in Headers */, + CE4DFAC922DC9A25002162E6 /* YGEnums.h in Headers */, + CE4DFAD422DC9A25002162E6 /* log.h in Headers */, + CE4DFAC422DC9A25002162E6 /* Yoga-internal.h in Headers */, + CE4DFAC122DC9A25002162E6 /* YGFloatOptional.h in Headers */, + CE4DFAD622DC9A25002162E6 /* YGConfig.h in Headers */, + CE4DFAD822DC9A25002162E6 /* YGMarker.h in Headers */, + CE4DFAC522DC9A25002162E6 /* YGLayout.h in Headers */, + CE4DFAC222DC9A25002162E6 /* Utils.h in Headers */, + CE4DFACB22DC9A25002162E6 /* YGNode.h in Headers */, + CE4DFAC322DC9A25002162E6 /* CompactValue.h in Headers */, + CE4DFAD722DC9A25002162E6 /* instrumentation.h in Headers */, + CE4DFAC822DC9A25002162E6 /* YGNodePrint.h in Headers */, + CE4DFAD222DC9A25002162E6 /* YGStyle.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CEF0E31622DC57560000C940 /* FlexLayout */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEF0E32B22DC57570000C940 /* Build configuration list for PBXNativeTarget "FlexLayout" */; + buildPhases = ( + CEF0E31222DC57560000C940 /* Headers */, + CEF0E31322DC57560000C940 /* Sources */, + CEF0E31422DC57560000C940 /* Frameworks */, + CEF0E31522DC57560000C940 /* Resources */, + CE4DFAE422DC9FD8002162E6 /* SwiftLint Script */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FlexLayout; + packageProductDependencies = ( + CEF0E39422DC59660000C940 /* Extension */, + ); + productName = FlexLayout; + productReference = CEF0E31722DC57560000C940 /* FlexLayout.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CEF0E30E22DC57560000C940 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "Songwen Ding"; + TargetAttributes = { + CEF0E31622DC57560000C940 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = CEF0E31122DC57560000C940 /* Build configuration list for PBXProject "FlexLayout" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CEF0E30D22DC57560000C940; + packageReferences = ( + CEF0E39322DC59660000C940 /* XCRemoteSwiftPackageReference "Extension" */, + ); + productRefGroup = CEF0E31822DC57560000C940 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CEF0E31622DC57560000C940 /* FlexLayout */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CEF0E31522DC57560000C940 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + CE4DFAE422DC9FD8002162E6 /* SwiftLint Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "SwiftLint Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CEF0E31322DC57560000C940 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE4DFAD322DC9A25002162E6 /* YGEnums.cpp in Sources */, + CE4DFAC622DC9A25002162E6 /* log.cpp in Sources */, + CE4DFAD122DC9A25002162E6 /* YGNodePrint.cpp in Sources */, + CE4DFAE222DC9B8F002162E6 /* YGLayout.m in Sources */, + CE4DFACA22DC9A25002162E6 /* YGLayout.cpp in Sources */, + CE4DFACE22DC9A25002162E6 /* YGStyle.cpp in Sources */, + CE4DFABF22DC9A25002162E6 /* CSS+Value.swift in Sources */, + CE4DFAD522DC9A25002162E6 /* YGValue.cpp in Sources */, + CE4DFACD22DC9A25002162E6 /* Yoga.cpp in Sources */, + CE4DFADF22DC9A25002162E6 /* FlexLayout.swift in Sources */, + CE4DFADA22DC9A25002162E6 /* YGNode.cpp in Sources */, + CE4DFAC022DC9A25002162E6 /* CSS+Types.swift in Sources */, + CE4DFAC722DC9A25002162E6 /* Utils.cpp in Sources */, + CE4DFADD22DC9A25002162E6 /* CSS.swift in Sources */, + CE4DFACF22DC9A25002162E6 /* YGMarker.cpp in Sources */, + CE4DFAD922DC9A25002162E6 /* YGConfig.cpp in Sources */, + CE4DFADE22DC9A25002162E6 /* NSValue+YGValue.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CEF0E32922DC57570000C940 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PROJECT_DIR}/FlexLayout/", + "${PROJECT_DIR}/Sources/", + "${PROJECT_DIR}/Sources/yoga", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + CEF0E32A22DC57570000C940 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PROJECT_DIR}/FlexLayout/", + "${PROJECT_DIR}/Sources/", + "${PROJECT_DIR}/Sources/yoga", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CEF0E32C22DC57570000C940 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = DD5P45UV7Y; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FlexLayout/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "-6166666843.FlexLayout"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEF0E32D22DC57570000C940 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = DD5P45UV7Y; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FlexLayout/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "-6166666843.FlexLayout"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CEF0E31122DC57560000C940 /* Build configuration list for PBXProject "FlexLayout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEF0E32922DC57570000C940 /* Debug */, + CEF0E32A22DC57570000C940 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CEF0E32B22DC57570000C940 /* Build configuration list for PBXNativeTarget "FlexLayout" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEF0E32C22DC57570000C940 /* Debug */, + CEF0E32D22DC57570000C940 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CEF0E39322DC59660000C940 /* XCRemoteSwiftPackageReference "Extension" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/DingSoung/Extension"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CEF0E39422DC59660000C940 /* Extension */ = { + isa = XCSwiftPackageProductDependency; + package = CEF0E39322DC59660000C940 /* XCRemoteSwiftPackageReference "Extension" */; + productName = Extension; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = CEF0E30E22DC57560000C940 /* Project object */; +} diff --git a/FlexLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/FlexLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/FlexLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..c4f4508 --- /dev/null +++ b/FlexLayout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "Extension", + "repositoryURL": "https://github.com/DingSoung/Extension", + "state": { + "branch": "master", + "revision": "1c3585d641ca93f4bfd3236edcc53ec3df9077b7", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/FlexLayout.xcodeproj/xcshareddata/xcschemes/FlexLayout.xcscheme b/FlexLayout.xcodeproj/xcshareddata/xcschemes/FlexLayout.xcscheme new file mode 100644 index 0000000..2d0bdd9 --- /dev/null +++ b/FlexLayout.xcodeproj/xcshareddata/xcschemes/FlexLayout.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FlexLayout/FlexLayout.h b/FlexLayout/FlexLayout.h new file mode 100644 index 0000000..5953b89 --- /dev/null +++ b/FlexLayout/FlexLayout.h @@ -0,0 +1,20 @@ +// +// FlexLayout.h +// FlexLayout +// +// Created by Songwen Ding on 2019/7/15. +// Copyright © 2019 Songwen Ding. All rights reserved. +// + +#import + +//! Project version number for FlexLayout. +FOUNDATION_EXPORT double FlexLayoutVersionNumber; + +//! Project version string for FlexLayout. +FOUNDATION_EXPORT const unsigned char FlexLayoutVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + +#import "Header.h" diff --git a/FlexLayout/Info.plist b/FlexLayout/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/FlexLayout/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..89de354 --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..b500fac --- /dev/null +++ b/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.0.0 + +import PackageDescription + +let package = Package( + name: "FlexLayout", + platforms: [ + .iOS(.v8) + ], + products: [ + .library(name: "FlexLayout", targets: ["FlexLayout"]) + ], + dependencies: [ + .package(url: "https://github.com/DingSoung/Extension", from: "0.8.9") + ], + targets: [ + .target(name: "FlexLayout", path: "Sources") + ], + swiftLanguageVersions: [ + .version("5.0.0") + ] +) diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..22c9c99 --- /dev/null +++ b/README.MD @@ -0,0 +1,41 @@ +![Release](https://img.shields.io/github/release/DingSoung/FlexLayout.svg) +![Status](https://travis-ci.org/DingSoung/FlexLayout.svg?branch=master) + +![SwiftPackage](https://img.shields.io/badge/SwiftPackage-compatible-E66848.svg?style=flat) + +![Language](https://img.shields.io/badge/Swift-5-FFAC45.svg?style=flat) + +[![Donate](https://img.shields.io/badge/Donate-PayPal-9EA59D.svg)](https://paypal.me/DingSongwen) + +A CSS Flex Layout for iOS + +# Install + +carthage + +```swift +github DingSoung/Extension.git master +``` + +# Usage + +```swift +let layout = FlexLayout() +layout.marginLeft = "10" +layout.width = 300 +layout.height = 300 +layout.marginTop = 100 +layout.alignItems = .flexStart +layout.flexDirection = .row +layout.borderWidth = "3" + +let layout2 = FlexLayout() +layout2.aspectRatio = 1 +layout2.width = 20% +layout2.margin = 8 + +layout.set(children: [layout2]) +_ = layout.intrinsicSize +print(layout.frame, layout.frame) +``` + diff --git a/Sources/CSS+Types.swift b/Sources/CSS+Types.swift new file mode 100644 index 0000000..094d274 --- /dev/null +++ b/Sources/CSS+Types.swift @@ -0,0 +1,167 @@ +// Created by Songwen Ding on 2019/4/16. +// Copyright © 2019 Songwen Ding. All rights reserved. + +import Foundation + +extension CSS { + public typealias Direction = YGDirection + public typealias FlexDirection = YGFlexDirection + public typealias Justify = YGJustify + public typealias Align = YGAlign + public typealias Position = YGPositionType + public typealias Wrap = YGWrap + public typealias Overflow = YGOverflow + public typealias Display = YGDisplay +} + +// MARK: - = support +extension CSS.Direction: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + switch value { + case "ltr": self = .LTR + case "rtl": self = .RTL + case "inherit": self = .inherit + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.FlexDirection: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "row": self = .row + case "row-reverse": self = .rowReverse + case "column": self = .column + case "column-reverse": self = .columnReverse + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Justify: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "flex-start": self = .flexStart + case "center": self = .center + case "flex-end": self = .flexEnd + case "space-between": self = .spaceBetween + case "space-around": self = .spaceAround + case "space-evenly": self = .spaceEvenly + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Align: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "auto": self = .auto + case "flex-start": self = .flexStart + case "center": self = .center + case "flex-end": self = .flexEnd + case "stretch": self = .stretch + case "baseline": self = .baseline + case "space-between": self = .spaceBetween + case "space-around": self = .spaceAround + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Position: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "relative": self = .relative + case "absolute": self = .absolute + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Wrap: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "nowrap": self = .noWrap + case "wrap": self = .wrap + case "wrap-reverse": self = .wrapReverse + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Overflow: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "visible": self = .visible + case "hidden": self = .hidden + case "scroll": self = .scroll + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +extension CSS.Display: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + switch value { + case "flex": self = .flex + case "none": self = .none + default: preconditionFailure("value: \(value) is invalid") + } + } +} + +// MARK: - dynamic setValue support +extension CSS.Direction { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.FlexDirection { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Justify { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Align { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Position { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Wrap { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Overflow { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} + +extension CSS.Display { + var nsValue: NSNumber { + return NSNumber(value: self.rawValue) + } +} diff --git a/Sources/CSS+Value.swift b/Sources/CSS+Value.swift new file mode 100644 index 0000000..51af1ef --- /dev/null +++ b/Sources/CSS+Value.swift @@ -0,0 +1,83 @@ +// Created by Songwen Ding on 2019/4/16. +// Copyright © 2019 Songwen Ding. All rights reserved. + +import Foundation +import CoreGraphics + +extension CSS { + public typealias Unit = YGUnit + public typealias Value = YGValue +} + +// MARK: = support +extension CSS.Value: ExpressibleByIntegerLiteral { + public typealias IntegerLiteralType = Int + public init(integerLiteral value: Int) { + self = CSS.Value(value: Float(value), unit: .point) + } +} + +extension CSS.Value: ExpressibleByFloatLiteral { + public typealias FloatLiteralType = Float + public init(floatLiteral value: Float) { + self = CSS.Value(value: Float(value), unit: .point) + } +} + +extension CSS.Value: ExpressibleByStringLiteral { + public typealias StringLiteralType = String + public init(stringLiteral value: String) { + var str = value.trimmingCharacters(in: .whitespacesAndNewlines) + if str == "auto" { + self = .auto + return + } else if str.last == "%" { + str = String(str.prefix(str.count - 1)) + if let number = Float(str) { + self = CSS.Value(value: number, unit: .percent) + return + } + } else { + if let number = Float(str) { + self = CSS.Value(value: number, unit: .point) + return + } + } + preconditionFailure("This value: \(value) is not invalid") + } +} + +// MARK: const value +extension CSS.Value { + public static let auto: CSS.Value = YGValueAuto + public static let undefined: CSS.Value = YGValueUndefined + public static let zero: CSS.Value = YGValueZero +} + +// MARK: kvo support +extension CSS.Value { + public var nsValue: NSValue { + return NSValue.valuewithYGValue(self) + } +} + +// MARK: % percent convert +postfix operator % + +extension Int { + public static postfix func % (value: Int) -> CSS.Value { + return CSS.Value(value: Float(value), unit: .percent) + } +} + +extension Float { + public static postfix func % (value: Float) -> CSS.Value { + return CSS.Value(value: value, unit: .percent) + } +} + +extension CGFloat { + public static postfix func % (value: CGFloat) -> CSS.Value { + return CSS.Value(value: Float(value), unit: .percent) + } +} diff --git a/Sources/CSS.swift b/Sources/CSS.swift new file mode 100644 index 0000000..ecff6fa --- /dev/null +++ b/Sources/CSS.swift @@ -0,0 +1,70 @@ +// Created by Songwen Ding on 2019/4/16. +// Copyright © 2019 Songwen Ding. All rights reserved. + +import Foundation +import CoreGraphics + +public struct CSS { +} + +extension CSS { + static var keyParaserMap: [String: (key: String, phrase: (String) -> Any?)] = [ + "direction": ("direction", { CSS.Direction(stringLiteral: $0).nsValue }), + "flex-direction": ("flexDirection", { CSS.FlexDirection(stringLiteral: $0).nsValue }), + "justify-content": ("justifyContent", { CSS.Justify(stringLiteral: $0).nsValue }), + "align-content": ("alignContent", { CSS.Align(stringLiteral: $0).nsValue }), + "align-items": ("alignItems", { CSS.Align(stringLiteral: $0).nsValue }), + "align-self": ("alignSelf", { CSS.Align(stringLiteral: $0).nsValue }), + "position": ("position", { CSS.Position(stringLiteral: $0).nsValue }), + "flex-wrap": ("flexWrap", { CSS.Wrap(stringLiteral: $0).nsValue }), + "overflow": ("overflow", { CSS.Overflow(stringLiteral: $0).nsValue }), + "display": ("display", { CSS.Display(stringLiteral: $0).nsValue }), + + "flex": ("flex", { Float($0) }), + "flex-grow": ("flexGrow", { Float($0) }), + "flex-shrink": ("flexShrink", { Float($0) }), + "flex-basis": ("flexBasis", { CSS.Value(stringLiteral: $0).nsValue }), + + "left": ("left", { CSS.Value(stringLiteral: $0).nsValue }), + "top": ("top", { CSS.Value(stringLiteral: $0).nsValue }), + "right": ("right", { CSS.Value(stringLiteral: $0).nsValue }), + "bottom": ("bottom", { CSS.Value(stringLiteral: $0).nsValue }), + "start": ("start", { CSS.Value(stringLiteral: $0).nsValue }), + "end": ("end", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-left": ("marginLeft", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-top": ("marginTop", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-right": ("marginRight", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-bottom": ("marginBottom", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-start": ("marginStart", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-end": ("marginEnd", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-horizontal": ("marginHorizontal", { CSS.Value(stringLiteral: $0).nsValue }), + "margin-vertica": ("marginVertica", { CSS.Value(stringLiteral: $0).nsValue }), + "margin": ("margin", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-left": ("paddingLeft", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-top": ("paddingTop", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-right": ("paddingRight", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-bottom": ("paddingBottom", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-start": ("paddingStart", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-end": ("paddingEnd", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-horizontal": ("paddingHorizontal", { CSS.Value(stringLiteral: $0).nsValue }), + "padding-vertical": ("paddingVertical", { CSS.Value(stringLiteral: $0).nsValue }), + "padding": ("padding", { CSS.Value(stringLiteral: $0).nsValue }), + + "border-left-width": ("borderLeftWidth", { Float($0) }), + "border-top-width": ("borderTopWidth", { Float($0) }), + "border-right-width": ("borderRightWidth", { Float($0) }), + "border-bottom-width": ("borderBottomWidth", { Float($0) }), + "border-start-width": ("borderStartWidth", { Float($0) }), + "border-end-width": ("borderEndWidth", { Float($0) }), + "border-width": ("borderWidth", { Float($0) }), + + "width": ("width", { CSS.Value(stringLiteral: $0).nsValue }), + "height": ("height", { CSS.Value(stringLiteral: $0).nsValue }), + "min-width": ("minWidth", { CSS.Value(stringLiteral: $0).nsValue }), + "min-height": ("minHeight", { CSS.Value(stringLiteral: $0).nsValue }), + "max-width": ("maxWidth", { CSS.Value(stringLiteral: $0).nsValue }), + "max-height": ("maxHeight", { CSS.Value(stringLiteral: $0).nsValue }), + + "aspect-ratio": ("aspectRatio", { Float($0) }) + ] +} diff --git a/Sources/FlexLayout.swift b/Sources/FlexLayout.swift new file mode 100644 index 0000000..60003cc --- /dev/null +++ b/Sources/FlexLayout.swift @@ -0,0 +1,346 @@ +// Created by Songwen Ding on 2019/4/16. +// Copyright © 2019 Songwen Ding. All rights reserved. + +import Foundation +import CoreGraphics +import Extension + +@objcMembers +open class FlexLayout: NSObject { + private class Context { + var sizeThatFits: (CGSize) -> CGSize + init(sizeThatFits: @escaping (CGSize) -> CGSize) { + self.sizeThatFits = sizeThatFits + } + } + + private var context: Context? + + private lazy var yoga: YGLayout = { + let yoga = YGLayout() + yoga.isEnabled = true + // Flex Layout Guide: http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html + return yoga + }() + + private var children = [FlexLayout]() +} + +extension FlexLayout { + @discardableResult + public final func set(children: [FlexLayout]) -> Self { + self.children = children + if self.isLeaf { + } else { + YGNodeSetMeasureFunc(self.yoga.node, nil) + let children = children.filter({ $0.yoga.isEnabled && $0.yoga.isIncludedInLayout }) + if !FlexLayout.nodeHasExactSameChildren(node: self.yoga.node, children: children) { + YGNodeRemoveAllChildren(self.yoga.node) + for index in 0.. CGSize) -> Self { + if self.isLeaf { + self.context = Context(sizeThatFits: sizeThatFits) + YGNodeSetContext(self.yoga.node, UnsafeMutableRawPointer(&self.context)) + YGNodeSetMeasureFunc(self.yoga.node, FlexLayout.measureFunc) + } else { + YGNodeSetMeasureFunc(self.yoga.node, nil) + } + return self + } +} + +extension FlexLayout { + private var isLeaf: Bool { + if self.yoga.isEnabled + && self.children.first(where: { $0.yoga.isEnabled && $0.yoga.isIncludedInLayout }) != nil { + return false + } + return true + } +} + +extension FlexLayout { + private static func nodeHasExactSameChildren(node: YGNodeRef, children: [FlexLayout]) -> Bool { + if YGNodeGetChildCount(node) != children.count { + return false + } + for index in 0.. YGSize = { + (node, width, widthMode, height, heightMode) in + let constrainedWidth = widthMode == .undefined ? Float.greatestFiniteMagnitude : width + let constrainedHeight = heightMode == .undefined ? Float.greatestFiniteMagnitude: height + var measureSize = CGSize.zero + if let context = YGNodeGetContext(node)?.assumingMemoryBound(to: Context.self).pointee { + measureSize = context.sizeThatFits(CGSize(width: constrainedWidth, height: constrainedHeight)) + } else { + warning("no measureFunc") + } + return YGSize(width: sanitizeMeasurement(constrainedWidth, Float(measureSize.width), widthMode), + height: sanitizeMeasurement(constrainedHeight, Float(measureSize.height), heightMode)) + } + + private static func sanitizeMeasurement(_ constrained: Float, _ measured: Float, _ mode: YGMeasureMode) -> Float { + switch mode { + case .exactly: + return constrained + case .atMost: + return min(constrained, measured) + case .undefined: + return measured + @unknown default: + fatalError() + } + } +} + +// MARK: - layout access +extension FlexLayout { + public final var intrinsicSize: CGSize { + return self.calculateLayout(size: CGSize(width: YGValue.undefined.value, + height: YGValue.undefined.value)) + } + + @discardableResult + public final func calculateLayout(size: CGSize) -> CGSize { + let node = self.yoga.node! + YGNodeCalculateLayout(node, + Float(size.width), + Float(size.height), + YGNodeStyleGetDirection(node)) + return CGSize(width: YGNodeLayoutGetWidth(node), + height: YGNodeLayoutGetHeight(node)) + } + + private static let screenScale = UIScreen.main.scale + @inline(__always) + private final func roundPixel(_ value: CGFloat) -> CGFloat { + return ceil(value * FlexLayout.screenScale) / FlexLayout.screenScale + } + + public var frame: CGRect { + let node = self.yoga.node + let frame = CGRect(x: YGNodeLayoutGetLeft(node), + y: YGNodeLayoutGetTop(node), + width: YGNodeLayoutGetWidth(node), + height: YGNodeLayoutGetHeight(node)) + let topLeft = frame.origin + let buttomRight = CGPoint(x: frame.maxX, y: frame.maxY) + return CGRect(x: roundPixel(topLeft.x), + y: roundPixel(topLeft.y), + width: roundPixel(buttomRight.x) - roundPixel(topLeft.x), + height: roundPixel(buttomRight.y) - roundPixel(topLeft.y) + ) + } +} + +// MARK: - programming config +extension FlexLayout { + public final var direction: CSS.Direction { + set { self.yoga.direction = newValue } get { return self.yoga.direction } + } + public final var flexDirection: CSS.FlexDirection { + set { self.yoga.flexDirection = newValue } get { return self.yoga.flexDirection } + } + public final var justifyContent: CSS.Justify { + set { self.yoga.justifyContent = newValue } get { return self.yoga.justifyContent } + } + public final var alignContent: CSS.Align { + set { self.yoga.alignContent = newValue } get { return self.yoga.alignContent } + } + public final var alignItems: CSS.Align { + set { self.yoga.alignItems = newValue } get { return self.yoga.alignItems } + } + public final var alignSelf: CSS.Align { + set { self.yoga.alignSelf = newValue } get { return self.yoga.alignSelf } + } + public final var position: CSS.Position { + set { self.yoga.position = newValue } get { return self.yoga.position } + } + public final var flexWrap: CSS.Wrap { + set { self.yoga.flexWrap = newValue } get { return self.yoga.flexWrap } + } + public final var overflow: CSS.Overflow { + set { self.yoga.overflow = newValue } get { return self.yoga.overflow } + } + public final var display: CSS.Display { + set { self.yoga.display = newValue } get { return self.yoga.display } + } + + public final var flex: CGFloat { + set { self.yoga.flex = newValue } get { return self.yoga.flex } + } + public final var flexGrow: CGFloat { + set { self.yoga.flexGrow = newValue } get { return self.yoga.flexGrow } + } + public final var flexShrink: CGFloat { + set { self.yoga.flexShrink = newValue } get { return self.yoga.flexShrink } + } + public final var flexBasis: CSS.Value { + set { self.yoga.flexBasis = newValue } get { return self.yoga.flexBasis } + } + + public final var left: CSS.Value { + set { self.yoga.left = newValue } get { return self.yoga.left } + } + public final var top: CSS.Value { + set { self.yoga.top = newValue } get { return self.yoga.top } + } + public final var right: CSS.Value { + set { self.yoga.right = newValue } get { return self.yoga.right } + } + public final var bottom: CSS.Value { + set { self.yoga.bottom = newValue } get { return self.yoga.bottom } + } + public final var start: CSS.Value { + set { self.yoga.start = newValue } get { return self.yoga.start } + } + public final var end: CSS.Value { + set { self.yoga.end = newValue } get { return self.yoga.end } + } + + public final var marginLeft: CSS.Value { + set { self.yoga.marginLeft = newValue } get { return self.yoga.marginLeft } + } + public final var marginTop: CSS.Value { + set { self.yoga.marginTop = newValue } get { return self.yoga.marginTop } + } + public final var marginRight: CSS.Value { + set { self.yoga.marginRight = newValue } get { return self.yoga.marginRight } + } + public final var marginBottom: CSS.Value { + set { self.yoga.marginBottom = newValue } get { return self.yoga.marginBottom } + } + public final var marginStart: CSS.Value { + set { self.yoga.marginStart = newValue } get { return self.yoga.marginStart } + } + public final var marginEnd: CSS.Value { + set { self.yoga.marginEnd = newValue } get { return self.yoga.marginEnd } + } + public final var marginHorizontal: CSS.Value { + set { self.yoga.marginHorizontal = newValue } get { return self.yoga.marginHorizontal } + } + public final var marginVertical: CSS.Value { + set { self.yoga.marginVertical = newValue } get { return self.yoga.marginVertical } + } + public final var margin: CSS.Value { + set { self.yoga.margin = newValue } get { return self.yoga.margin } + } + + public final var paddingLeft: CSS.Value { + set { self.yoga.paddingLeft = newValue } get { return self.yoga.paddingLeft } + } + public final var paddingTop: CSS.Value { + set { self.yoga.paddingTop = newValue } get { return self.yoga.paddingTop } + } + public final var paddingRight: CSS.Value { + set { self.yoga.paddingRight = newValue } get { return self.yoga.paddingRight } + } + public final var paddingBottom: CSS.Value { + set { self.yoga.paddingBottom = newValue } get { return self.yoga.paddingBottom } + } + public final var paddingStart: CSS.Value { + set { self.yoga.paddingStart = newValue } get { return self.yoga.paddingStart } + } + public final var paddingEnd: CSS.Value { + set { self.yoga.paddingEnd = newValue } get { return self.yoga.paddingEnd } + } + public final var paddingHorizontal: CSS.Value { + set { self.yoga.paddingHorizontal = newValue } get { return self.yoga.paddingHorizontal } + } + public final var paddingVertical: CSS.Value { + set { self.yoga.paddingVertical = newValue } get { return self.yoga.paddingVertical } + } + public final var padding: CSS.Value { + set { self.yoga.padding = newValue } get { return self.yoga.padding } + } + + public final var borderLeftWidth: CGFloat { + set { self.yoga.borderLeftWidth = newValue } get { return self.yoga.borderLeftWidth } + } + public final var borderTopWidth: CGFloat { + set { self.yoga.borderTopWidth = newValue } get { return self.yoga.borderTopWidth } + } + public final var borderRightWidth: CGFloat { + set { self.yoga.borderRightWidth = newValue } get { return self.yoga.borderRightWidth } + } + public final var borderBottomWidth: CGFloat { + set { self.yoga.borderBottomWidth = newValue } get { return self.yoga.borderBottomWidth } + } + public final var borderStartWidth: CGFloat { + set { self.yoga.borderStartWidth = newValue } get { return self.yoga.borderStartWidth } + } + public final var borderEndWidth: CGFloat { + set { self.yoga.borderEndWidth = newValue } get { return self.yoga.borderEndWidth } + } + public final var borderWidth: CGFloat { + set { self.yoga.borderWidth = newValue } get { return self.yoga.borderWidth } + } + + public final var width: CSS.Value { + set { self.yoga.width = newValue } get { return self.yoga.width } + } + public final var height: CSS.Value { + set { self.yoga.height = newValue } get { return self.yoga.height } + } + public final var minWidth: CSS.Value { + set { self.yoga.minWidth = newValue } get { return self.yoga.minWidth } + } + public final var minHeight: CSS.Value { + set { self.yoga.minHeight = newValue } get { return self.yoga.minHeight } + } + public final var maxWidth: CSS.Value { + set { self.yoga.maxWidth = newValue } get { return self.yoga.maxWidth } + } + public final var maxHeight: CSS.Value { + set { self.yoga.maxHeight = newValue } get { return self.yoga.maxHeight } + } + + public final var aspectRatio: CGFloat { + set { self.yoga.aspectRatio = newValue } get { return self.yoga.aspectRatio } + } + public final var resolvedDirection: CSS.Direction { + return self.yoga.resolvedDirection + } +} + +// MARK: - dynamic config layout +extension FlexLayout { + @discardableResult + public final func set(layout: [String: String]) -> Self { + layout.forEach { (pair) in + if let keyPhraser = CSS.keyParaserMap[pair.key] { + if let value = keyPhraser.phrase(pair.value) { + self.yoga.setValue(value, forKey: keyPhraser.key) + } else { + error("can not phrase style:", pair) + } + } else { + error("no phraser for style:", pair) + } + } + return self + } +} + +extension YGLayout { + open override func setValue(_ value: Any?, forUndefinedKey key: String) { + super.setValue(value, forUndefinedKey: key) + error("can not apply style:", key, value ?? "nil") + } +} diff --git a/Sources/Header.h b/Sources/Header.h new file mode 100644 index 0000000..24f508c --- /dev/null +++ b/Sources/Header.h @@ -0,0 +1,14 @@ +// Created by Songwen Ding on 2019/4/16. +// Copyright © 2019 Songwen Ding. All rights reserved. + +#ifndef Header_h +#define Header_h + +#import "YGEnums.h" +#import "Yoga.h" +#import "YGMacros.h" + +#import "YGLayout.h" +#import "NSValue+YGValue.h" + +#endif /* Header_h */ diff --git a/Sources/NSValue+YGValue.h b/Sources/NSValue+YGValue.h new file mode 100644 index 0000000..a087442 --- /dev/null +++ b/Sources/NSValue+YGValue.h @@ -0,0 +1,18 @@ +// Created by Songwen Ding on 2019/5/30. +// Copyright © 2019 Songwen Ding. All rights reserved. + +#import +#import "YGValue.h" + + +NS_ASSUME_NONNULL_BEGIN + +@interface NSValue (YGValue) + ++ (instancetype)valuewithYGValue:(YGValue)value; + +@property (readonly) YGValue ygValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/NSValue+YGValue.m b/Sources/NSValue+YGValue.m new file mode 100644 index 0000000..58309c0 --- /dev/null +++ b/Sources/NSValue+YGValue.m @@ -0,0 +1,19 @@ +// Created by Songwen Ding on 2019/5/30. +// Copyright © 2019 Songwen Ding. All rights reserved. + +#import "NSValue+YGValue.h" + +@implementation NSValue (YGValue) + ++ (instancetype)valuewithYGValue:(YGValue)value { + return [self valueWithBytes:&value objCType:@encode(YGValue)]; +} + +- (YGValue)ygValue { + YGValue value; + [self getValue:&value]; + return value; +} + +@end + diff --git a/Sources/YGLayout.h b/Sources/YGLayout.h new file mode 100755 index 0000000..d5a5907 --- /dev/null +++ b/Sources/YGLayout.h @@ -0,0 +1,97 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +#import "YGEnums.h" +#import "Yoga.h" +#import "YGMacros.h" + +@interface YGLayout : NSObject + +@property (nonatomic, assign, readonly) YGNodeRef node; + +/** + The property that decides if we should include this view when calculating + layout. Defaults to YES. + */ +@property (nonatomic, readwrite, assign, setter=setIncludedInLayout:) BOOL isIncludedInLayout; + +/** + The property that decides during layout/sizing whether or not styling properties should be applied. + Defaults to NO. + */ +@property (nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled; + +@property (nonatomic, readwrite, assign) YGDirection direction; +@property (nonatomic, readwrite, assign) YGFlexDirection flexDirection; +@property (nonatomic, readwrite, assign) YGJustify justifyContent; +@property (nonatomic, readwrite, assign) YGAlign alignContent; +@property (nonatomic, readwrite, assign) YGAlign alignItems; +@property (nonatomic, readwrite, assign) YGAlign alignSelf; +@property (nonatomic, readwrite, assign) YGPositionType position; +@property (nonatomic, readwrite, assign) YGWrap flexWrap; +@property (nonatomic, readwrite, assign) YGOverflow overflow; +@property (nonatomic, readwrite, assign) YGDisplay display; + +@property (nonatomic, readwrite, assign) CGFloat flex; +@property (nonatomic, readwrite, assign) CGFloat flexGrow; +@property (nonatomic, readwrite, assign) CGFloat flexShrink; +@property (nonatomic, readwrite, assign) YGValue flexBasis; + +@property (nonatomic, readwrite, assign) YGValue left; +@property (nonatomic, readwrite, assign) YGValue top; +@property (nonatomic, readwrite, assign) YGValue right; +@property (nonatomic, readwrite, assign) YGValue bottom; +@property (nonatomic, readwrite, assign) YGValue start; +@property (nonatomic, readwrite, assign) YGValue end; + +@property (nonatomic, readwrite, assign) YGValue marginLeft; +@property (nonatomic, readwrite, assign) YGValue marginTop; +@property (nonatomic, readwrite, assign) YGValue marginRight; +@property (nonatomic, readwrite, assign) YGValue marginBottom; +@property (nonatomic, readwrite, assign) YGValue marginStart; +@property (nonatomic, readwrite, assign) YGValue marginEnd; +@property (nonatomic, readwrite, assign) YGValue marginHorizontal; +@property (nonatomic, readwrite, assign) YGValue marginVertical; +@property (nonatomic, readwrite, assign) YGValue margin; + +@property (nonatomic, readwrite, assign) YGValue paddingLeft; +@property (nonatomic, readwrite, assign) YGValue paddingTop; +@property (nonatomic, readwrite, assign) YGValue paddingRight; +@property (nonatomic, readwrite, assign) YGValue paddingBottom; +@property (nonatomic, readwrite, assign) YGValue paddingStart; +@property (nonatomic, readwrite, assign) YGValue paddingEnd; +@property (nonatomic, readwrite, assign) YGValue paddingHorizontal; +@property (nonatomic, readwrite, assign) YGValue paddingVertical; +@property (nonatomic, readwrite, assign) YGValue padding; + +@property (nonatomic, readwrite, assign) CGFloat borderLeftWidth; +@property (nonatomic, readwrite, assign) CGFloat borderTopWidth; +@property (nonatomic, readwrite, assign) CGFloat borderRightWidth; +@property (nonatomic, readwrite, assign) CGFloat borderBottomWidth; +@property (nonatomic, readwrite, assign) CGFloat borderStartWidth; +@property (nonatomic, readwrite, assign) CGFloat borderEndWidth; +@property (nonatomic, readwrite, assign) CGFloat borderWidth; + +@property (nonatomic, readwrite, assign) YGValue width; +@property (nonatomic, readwrite, assign) YGValue height; +@property (nonatomic, readwrite, assign) YGValue minWidth; +@property (nonatomic, readwrite, assign) YGValue minHeight; +@property (nonatomic, readwrite, assign) YGValue maxWidth; +@property (nonatomic, readwrite, assign) YGValue maxHeight; + +// Yoga specific properties, not compatible with flexbox specification +@property (nonatomic, readwrite, assign) CGFloat aspectRatio; + +/** + Get the resolved direction of this node. This won't be YGDirectionInherit + */ +@property (nonatomic, readonly, assign) YGDirection resolvedDirection; + +@end diff --git a/Sources/YGLayout.m b/Sources/YGLayout.m new file mode 100755 index 0000000..a696016 --- /dev/null +++ b/Sources/YGLayout.m @@ -0,0 +1,212 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "YGLayout.h" +#import +#import + +#define YG_PROPERTY(type, lowercased_name, capitalized_name) \ +- (type)lowercased_name \ +{ \ + return YGNodeStyleGet##capitalized_name(self.node); \ +} \ + \ +- (void)set##capitalized_name:(type)lowercased_name \ +{ \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \ +} + +#define YG_VALUE_PROPERTY(lowercased_name, capitalized_name) \ +- (YGValue)lowercased_name \ +{ \ + return YGNodeStyleGet##capitalized_name(self.node); \ +} \ + \ +- (void)set##capitalized_name:(YGValue)lowercased_name \ +{ \ + switch (lowercased_name.unit) { \ + case YGUnitUndefined: \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ + break; \ + case YGUnitPoint: \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ + break; \ + case YGUnitPercent: \ + YGNodeStyleSet##capitalized_name##Percent(self.node, lowercased_name.value); \ + break; \ + default: \ + NSAssert(NO, @"Not implemented"); \ + } \ +} + +#define YG_AUTO_VALUE_PROPERTY(lowercased_name, capitalized_name) \ +- (YGValue)lowercased_name \ +{ \ + return YGNodeStyleGet##capitalized_name(self.node); \ +} \ + \ +- (void)set##capitalized_name:(YGValue)lowercased_name \ +{ \ + switch (lowercased_name.unit) { \ + case YGUnitPoint: \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name.value); \ + break; \ + case YGUnitPercent: \ + YGNodeStyleSet##capitalized_name##Percent(self.node, lowercased_name.value); \ + break; \ + case YGUnitAuto: \ + YGNodeStyleSet##capitalized_name##Auto(self.node); \ + break; \ + default: \ + NSAssert(NO, @"Not implemented"); \ + } \ +} + +#define YG_EDGE_PROPERTY_GETTER(type, lowercased_name, capitalized_name, property, edge) \ +- (type)lowercased_name \ +{ \ + return YGNodeStyleGet##property(self.node, edge); \ +} + +#define YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) \ +- (void)set##capitalized_name:(CGFloat)lowercased_name \ +{ \ + YGNodeStyleSet##property(self.node, edge, lowercased_name); \ +} + +#define YG_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \ +YG_EDGE_PROPERTY_GETTER(CGFloat, lowercased_name, capitalized_name, property, edge) \ +YG_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) + +#define YG_VALUE_EDGE_PROPERTY_SETTER(objc_lowercased_name, objc_capitalized_name, c_name, edge) \ +- (void)set##objc_capitalized_name:(YGValue)objc_lowercased_name \ +{ \ + switch (objc_lowercased_name.unit) { \ + case YGUnitUndefined: \ + YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \ + break; \ + case YGUnitPoint: \ + YGNodeStyleSet##c_name(self.node, edge, objc_lowercased_name.value); \ + break; \ + case YGUnitPercent: \ + YGNodeStyleSet##c_name##Percent(self.node, edge, objc_lowercased_name.value); \ + break; \ + default: \ + NSAssert(NO, @"Not implemented"); \ + } \ +} + +#define YG_VALUE_EDGE_PROPERTY(lowercased_name, capitalized_name, property, edge) \ +YG_EDGE_PROPERTY_GETTER(YGValue, lowercased_name, capitalized_name, property, edge) \ +YG_VALUE_EDGE_PROPERTY_SETTER(lowercased_name, capitalized_name, property, edge) + +#define YG_VALUE_EDGES_PROPERTIES(lowercased_name, capitalized_name) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Left, capitalized_name##Left, capitalized_name, YGEdgeLeft) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Top, capitalized_name##Top, capitalized_name, YGEdgeTop) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Right, capitalized_name##Right, capitalized_name, YGEdgeRight) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Bottom, capitalized_name##Bottom, capitalized_name, YGEdgeBottom) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Start, capitalized_name##Start, capitalized_name, YGEdgeStart) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##End, capitalized_name##End, capitalized_name, YGEdgeEnd) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Horizontal, capitalized_name##Horizontal, capitalized_name, YGEdgeHorizontal) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name##Vertical, capitalized_name##Vertical, capitalized_name, YGEdgeVertical) \ +YG_VALUE_EDGE_PROPERTY(lowercased_name, capitalized_name, capitalized_name, YGEdgeAll) + +static YGConfigRef globalConfig; + +@interface YGLayout () + +@end + +@implementation YGLayout + +@synthesize isEnabled=_isEnabled; +@synthesize isIncludedInLayout=_isIncludedInLayout; +@synthesize node=_node; + ++ (void)initialize +{ + globalConfig = YGConfigNew(); + YGConfigSetExperimentalFeatureEnabled(globalConfig, YGExperimentalFeatureWebFlexBasis, true); + YGConfigSetPointScaleFactor(globalConfig, [UIScreen mainScreen].scale); +} + +- (instancetype)init +{ + if (self = [super init]) { + _node = YGNodeNewWithConfig(globalConfig); + _isEnabled = NO; + _isIncludedInLayout = YES; + } + + return self; +} + +- (void)dealloc +{ + YGNodeFree(self.node); +} + +#pragma mark - Style + +- (YGPositionType)position +{ + return YGNodeStyleGetPositionType(self.node); +} + +- (void)setPosition:(YGPositionType)position +{ + YGNodeStyleSetPositionType(self.node, position); +} + +YG_PROPERTY(YGDirection, direction, Direction) +YG_PROPERTY(YGFlexDirection, flexDirection, FlexDirection) +YG_PROPERTY(YGJustify, justifyContent, JustifyContent) +YG_PROPERTY(YGAlign, alignContent, AlignContent) +YG_PROPERTY(YGAlign, alignItems, AlignItems) +YG_PROPERTY(YGAlign, alignSelf, AlignSelf) +YG_PROPERTY(YGWrap, flexWrap, FlexWrap) +YG_PROPERTY(YGOverflow, overflow, Overflow) +YG_PROPERTY(YGDisplay, display, Display) + +YG_PROPERTY(CGFloat, flex, Flex) +YG_PROPERTY(CGFloat, flexGrow, FlexGrow) +YG_PROPERTY(CGFloat, flexShrink, FlexShrink) +YG_AUTO_VALUE_PROPERTY(flexBasis, FlexBasis) + +YG_VALUE_EDGE_PROPERTY(left, Left, Position, YGEdgeLeft) +YG_VALUE_EDGE_PROPERTY(top, Top, Position, YGEdgeTop) +YG_VALUE_EDGE_PROPERTY(right, Right, Position, YGEdgeRight) +YG_VALUE_EDGE_PROPERTY(bottom, Bottom, Position, YGEdgeBottom) +YG_VALUE_EDGE_PROPERTY(start, Start, Position, YGEdgeStart) +YG_VALUE_EDGE_PROPERTY(end, End, Position, YGEdgeEnd) +YG_VALUE_EDGES_PROPERTIES(margin, Margin) +YG_VALUE_EDGES_PROPERTIES(padding, Padding) + +YG_EDGE_PROPERTY(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft) +YG_EDGE_PROPERTY(borderTopWidth, BorderTopWidth, Border, YGEdgeTop) +YG_EDGE_PROPERTY(borderRightWidth, BorderRightWidth, Border, YGEdgeRight) +YG_EDGE_PROPERTY(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom) +YG_EDGE_PROPERTY(borderStartWidth, BorderStartWidth, Border, YGEdgeStart) +YG_EDGE_PROPERTY(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd) +YG_EDGE_PROPERTY(borderWidth, BorderWidth, Border, YGEdgeAll) + +YG_AUTO_VALUE_PROPERTY(width, Width) +YG_AUTO_VALUE_PROPERTY(height, Height) +YG_VALUE_PROPERTY(minWidth, MinWidth) +YG_VALUE_PROPERTY(minHeight, MinHeight) +YG_VALUE_PROPERTY(maxWidth, MaxWidth) +YG_VALUE_PROPERTY(maxHeight, MaxHeight) +YG_PROPERTY(CGFloat, aspectRatio, AspectRatio) + +#pragma mark - Layout and Sizing + +- (YGDirection)resolvedDirection +{ + return YGNodeLayoutGetDirection(self.node); +} + +@end diff --git a/Sources/yoga/CompactValue.h b/Sources/yoga/CompactValue.h new file mode 100755 index 0000000..3cc36a4 --- /dev/null +++ b/Sources/yoga/CompactValue.h @@ -0,0 +1,187 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGValue.h" + +#include +#include +#include + +static_assert( + std::numeric_limits::is_iec559, + "facebook::yoga::detail::CompactValue only works with IEEE754 floats"); + +#ifdef YOGA_COMPACT_VALUE_TEST +#define VISIBLE_FOR_TESTING public: +#else +#define VISIBLE_FOR_TESTING private: +#endif + +namespace facebook { +namespace yoga { +namespace detail { + +// This class stores YGValue in 32 bits. +// - The value does not matter for Undefined and Auto. NaNs are used for their +// representation. +// - To differentiate between Point and Percent, one exponent bit is used. +// Supported the range [0x40, 0xbf] (0xbf is inclusive for point, but +// exclusive for percent). +// - Value ranges: +// points: 1.08420217e-19f to 36893485948395847680 +// 0x00000000 0x3fffffff +// percent: 1.08420217e-19f to 18446742974197923840 +// 0x40000000 0x7f7fffff +// - Zero is supported, negative zero is not +// - values outside of the representable range are clamped +class CompactValue { + friend constexpr bool operator==(CompactValue, CompactValue) noexcept; + +public: + static constexpr auto LOWER_BOUND = 1.08420217e-19f; + static constexpr auto UPPER_BOUND_POINT = 36893485948395847680.0f; + static constexpr auto UPPER_BOUND_PERCENT = 18446742974197923840.0f; + + template + static CompactValue of(float value) noexcept { + if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) { + constexpr auto zero = + Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT; + return {Payload{zero}}; + } + + constexpr auto upperBound = + Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT; + if (value > upperBound || value < -upperBound) { + value = copysignf(upperBound, value); + } + + uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0; + auto data = Payload{value}; + data.repr -= BIAS; + data.repr |= unitBit; + return {data}; + } + + template + static CompactValue ofMaybe(float value) noexcept { + return std::isnan(value) || std::isinf(value) ? ofUndefined() + : of(value); + } + + static constexpr CompactValue ofZero() noexcept { + return CompactValue{Payload{ZERO_BITS_POINT}}; + } + + static constexpr CompactValue ofUndefined() noexcept { + return CompactValue{}; + } + + static constexpr CompactValue ofAuto() noexcept { + return CompactValue{Payload{AUTO_BITS}}; + } + + constexpr CompactValue() noexcept + : payload_(std::numeric_limits::quiet_NaN()) {} + + CompactValue(const YGValue& x) noexcept : payload_(uint32_t{0}) { + switch (x.unit) { + case YGUnitUndefined: + *this = ofUndefined(); + break; + case YGUnitAuto: + *this = ofAuto(); + break; + case YGUnitPoint: + *this = of(x.value); + break; + case YGUnitPercent: + *this = of(x.value); + break; + } + } + + operator YGValue() const noexcept { + switch (payload_.repr) { + case AUTO_BITS: + return YGValueAuto; + case ZERO_BITS_POINT: + return YGValue{0.0f, YGUnitPoint}; + case ZERO_BITS_PERCENT: + return YGValue{0.0f, YGUnitPercent}; + } + + if (std::isnan(payload_.value)) { + return YGValueUndefined; + } + + auto data = payload_; + data.repr &= ~PERCENT_BIT; + data.repr += BIAS; + + return YGValue{data.value, + payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint}; + } + + bool isUndefined() const noexcept { + return ( + payload_.repr != AUTO_BITS && payload_.repr != ZERO_BITS_POINT && + payload_.repr != ZERO_BITS_PERCENT && std::isnan(payload_.value)); + } + + bool isAuto() const noexcept { + return payload_.repr == AUTO_BITS; + } + +private: + union Payload { + float value; + uint32_t repr; + Payload() = delete; + constexpr Payload(uint32_t r) : repr(r) {} + constexpr Payload(float v) : value(v) {} + }; + + static constexpr uint32_t BIAS = 0x20000000; + static constexpr uint32_t PERCENT_BIT = 0x40000000; + + // these are signaling NaNs with specific bit pattern as payload they will be + // silenced whenever going through an FPU operation on ARM + x86 + static constexpr uint32_t AUTO_BITS = 0x7faaaaaa; + static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f; + static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0; + + constexpr CompactValue(Payload data) noexcept : payload_(data) {} + + Payload payload_; + + VISIBLE_FOR_TESTING uint32_t repr() { + return payload_.repr; + } +}; + +template <> +CompactValue CompactValue::of(float) noexcept = delete; +template <> +CompactValue CompactValue::of(float) noexcept = delete; +template <> +CompactValue CompactValue::ofMaybe(float) noexcept = delete; +template <> +CompactValue CompactValue::ofMaybe(float) noexcept = delete; + +constexpr bool operator==(CompactValue a, CompactValue b) noexcept { + return a.payload_.repr == b.payload_.repr; +} + +constexpr bool operator!=(CompactValue a, CompactValue b) noexcept { + return !(a == b); +} + +} // namespace detail +} // namespace yoga +} // namespace facebook diff --git a/Sources/yoga/Utils.cpp b/Sources/yoga/Utils.cpp new file mode 100755 index 0000000..38b686c --- /dev/null +++ b/Sources/yoga/Utils.cpp @@ -0,0 +1,66 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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" + +using namespace facebook; + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction) { + return YGFlexDirectionIsColumn(flexDirection) + ? YGResolveFlexDirection(YGFlexDirectionRow, direction) + : YGFlexDirectionColumn; +} + +float YGFloatMax(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fmaxf(a, b); + } + return yoga::isUndefined(a) ? b : a; +} + +float YGFloatMin(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fminf(a, b); + } + + return yoga::isUndefined(a) ? b : a; +} + +bool YGValueEqual(const YGValue& a, const YGValue& b) { + if (a.unit != b.unit) { + return false; + } + + if (a.unit == YGUnitUndefined || + (yoga::isUndefined(a.value) && yoga::isUndefined(b.value))) { + return true; + } + + return fabs(a.value - b.value) < 0.0001f; +} + +bool YGFloatsEqual(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fabs(a - b) < 0.0001f; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +float YGFloatSanitize(const float val) { + return yoga::isUndefined(val) ? 0 : val; +} + +YGFloatOptional YGFloatOptionalMax(YGFloatOptional op1, YGFloatOptional op2) { + if (op1 >= op2) { + return op1; + } + if (op2 > op1) { + return op2; + } + return op1.isUndefined() ? op2 : op1; +} diff --git a/Sources/yoga/Utils.h b/Sources/yoga/Utils.h new file mode 100755 index 0000000..900ccb1 --- /dev/null +++ b/Sources/yoga/Utils.h @@ -0,0 +1,142 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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" +#include "CompactValue.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; + uint32_t 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 owner. + 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); +inline bool YGValueEqual( + facebook::yoga::detail::CompactValue a, + facebook::yoga::detail::CompactValue b) { + return YGValueEqual((YGValue) a, (YGValue) b); +} + +// This custom float equality function returns true if either absolute +// difference between two floats is less than 0.0001f or both are undefined. +bool YGFloatsEqual(const float a, const float b); + +float YGFloatMax(const float a, const float b); + +YGFloatOptional YGFloatOptionalMax( + const YGFloatOptional op1, + const YGFloatOptional op2); + +float YGFloatMin(const float a, const float b); + +// This custom float comparision function compares the array of float with +// YGFloatsEqual, as the default float comparision operator will not work(Look +// at the comments of YGFloatsEqual function). +template +bool YGFloatArrayEqual( + const std::array& val1, + const std::array& val2) { + bool areEqual = true; + for (std::size_t i = 0; i < size && areEqual; ++i) { + areEqual = YGFloatsEqual(val1[i], val2[i]); + } + return areEqual; +} + +// This function returns 0 if YGFloatIsUndefined(val) is true and val otherwise +float YGFloatSanitize(const float val); + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction); + +inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionRow || + flexDirection == YGFlexDirectionRowReverse; +} + +inline YGFloatOptional YGResolveValue( + const YGValue value, + const float ownerSize) { + switch (value.unit) { + case YGUnitPoint: + return YGFloatOptional{value.value}; + case YGUnitPercent: + return YGFloatOptional{value.value * ownerSize * 0.01f}; + default: + return YGFloatOptional{}; + } +} + +inline YGFloatOptional YGResolveValue( + yoga::detail::CompactValue value, + float ownerSize) { + return YGResolveValue((YGValue) value, ownerSize); +} + +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; +} + +inline YGFloatOptional YGResolveValueMargin( + yoga::detail::CompactValue value, + const float ownerSize) { + return value.isAuto() ? YGFloatOptional{0} : YGResolveValue(value, ownerSize); +} diff --git a/Sources/yoga/YGConfig.cpp b/Sources/yoga/YGConfig.cpp new file mode 100755 index 0000000..773ad24 --- /dev/null +++ b/Sources/yoga/YGConfig.cpp @@ -0,0 +1,43 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGConfig.h" + +YGConfig::YGConfig(YGLogger logger) : cloneNodeCallback_{nullptr} { + logger_.noContext = logger; + loggerUsesContext_ = false; +} + +void YGConfig::log( + YGConfig* config, + YGNode* node, + YGLogLevel logLevel, + void* logContext, + const char* format, + va_list args) { + if (loggerUsesContext_) { + logger_.withContext(config, node, logLevel, logContext, format, args); + } else { + logger_.noContext(config, node, logLevel, format, args); + } +} + +YGNodeRef YGConfig::cloneNode( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext) { + YGNodeRef clone = nullptr; + if (cloneNodeCallback_.noContext != nullptr) { + clone = cloneNodeUsesContext_ + ? cloneNodeCallback_.withContext(node, owner, childIndex, cloneContext) + : cloneNodeCallback_.noContext(node, owner, childIndex); + } + if (clone == nullptr) { + clone = YGNodeClone(node); + } + return clone; +} diff --git a/Sources/yoga/YGConfig.h b/Sources/yoga/YGConfig.h new file mode 100755 index 0000000..e0f29c5 --- /dev/null +++ b/Sources/yoga/YGConfig.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGMarker.h" +#include "Yoga-internal.h" +#include "Yoga.h" + +struct YGConfig { + using LogWithContextFn = int (*)( + YGConfigRef config, + YGNodeRef node, + YGLogLevel level, + void* context, + const char* format, + va_list args); + using CloneWithContextFn = YGNodeRef (*)( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext); + +private: + union { + CloneWithContextFn withContext; + YGCloneNodeFunc noContext; + } cloneNodeCallback_; + union { + LogWithContextFn withContext; + YGLogger noContext; + } logger_; + bool cloneNodeUsesContext_; + bool loggerUsesContext_; + +public: + bool useWebDefaults = false; + bool useLegacyStretchBehaviour = false; + bool shouldDiffLayoutWithoutLegacyStretchBehaviour = false; + bool printTree = false; + float pointScaleFactor = 1.0f; + std::array()> + experimentalFeatures = {}; + void* context = nullptr; + YGMarkerCallbacks markerCallbacks = {nullptr, nullptr}; + + YGConfig(YGLogger logger); + void log(YGConfig*, YGNode*, YGLogLevel, void*, const char*, va_list); + void setLogger(YGLogger logger) { + logger_.noContext = logger; + loggerUsesContext_ = false; + } + void setLogger(LogWithContextFn logger) { + logger_.withContext = logger; + loggerUsesContext_ = true; + } + void setLogger(std::nullptr_t) { + setLogger(YGLogger{nullptr}); + } + + YGNodeRef cloneNode( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext); + void setCloneNodeCallback(YGCloneNodeFunc cloneNode) { + cloneNodeCallback_.noContext = cloneNode; + cloneNodeUsesContext_ = false; + } + void setCloneNodeCallback(CloneWithContextFn cloneNode) { + cloneNodeCallback_.withContext = cloneNode; + cloneNodeUsesContext_ = true; + } + void setCloneNodeCallback(std::nullptr_t) { + setCloneNodeCallback(YGCloneNodeFunc{nullptr}); + } +}; diff --git a/Sources/yoga/YGEnums.cpp b/Sources/yoga/YGEnums.cpp new file mode 100755 index 0000000..ff4b130 --- /dev/null +++ b/Sources/yoga/YGEnums.cpp @@ -0,0 +1,225 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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" + +const char* YGAlignToString(const YGAlign value) { + switch (value) { + case YGAlignAuto: + return "auto"; + case YGAlignFlexStart: + return "flex-start"; + case YGAlignCenter: + return "center"; + case YGAlignFlexEnd: + return "flex-end"; + case YGAlignStretch: + return "stretch"; + case YGAlignBaseline: + return "baseline"; + case YGAlignSpaceBetween: + return "space-between"; + case YGAlignSpaceAround: + return "space-around"; + } + return "unknown"; +} + +const char* YGDimensionToString(const YGDimension value) { + switch (value) { + case YGDimensionWidth: + return "width"; + case YGDimensionHeight: + return "height"; + } + return "unknown"; +} + +const char* YGDirectionToString(const YGDirection value) { + switch (value) { + case YGDirectionInherit: + return "inherit"; + case YGDirectionLTR: + return "ltr"; + case YGDirectionRTL: + return "rtl"; + } + return "unknown"; +} + +const char* YGDisplayToString(const YGDisplay value) { + switch (value) { + case YGDisplayFlex: + return "flex"; + case YGDisplayNone: + return "none"; + } + return "unknown"; +} + +const char* YGEdgeToString(const YGEdge value) { + switch (value) { + case YGEdgeLeft: + return "left"; + case YGEdgeTop: + return "top"; + case YGEdgeRight: + return "right"; + case YGEdgeBottom: + return "bottom"; + case YGEdgeStart: + return "start"; + case YGEdgeEnd: + return "end"; + case YGEdgeHorizontal: + return "horizontal"; + case YGEdgeVertical: + return "vertical"; + case YGEdgeAll: + return "all"; + } + return "unknown"; +} + +const char* YGExperimentalFeatureToString(const YGExperimentalFeature value) { + switch (value) { + case YGExperimentalFeatureWebFlexBasis: + return "web-flex-basis"; + } + return "unknown"; +} + +const char* YGFlexDirectionToString(const YGFlexDirection value) { + switch (value) { + case YGFlexDirectionColumn: + return "column"; + case YGFlexDirectionColumnReverse: + return "column-reverse"; + case YGFlexDirectionRow: + return "row"; + case YGFlexDirectionRowReverse: + return "row-reverse"; + } + return "unknown"; +} + +const char* YGJustifyToString(const YGJustify value) { + switch (value) { + case YGJustifyFlexStart: + return "flex-start"; + case YGJustifyCenter: + return "center"; + case YGJustifyFlexEnd: + return "flex-end"; + case YGJustifySpaceBetween: + return "space-between"; + case YGJustifySpaceAround: + return "space-around"; + case YGJustifySpaceEvenly: + return "space-evenly"; + } + return "unknown"; +} + +const char* YGLogLevelToString(const YGLogLevel value) { + switch (value) { + case YGLogLevelError: + return "error"; + case YGLogLevelWarn: + return "warn"; + case YGLogLevelInfo: + return "info"; + case YGLogLevelDebug: + return "debug"; + case YGLogLevelVerbose: + return "verbose"; + case YGLogLevelFatal: + return "fatal"; + } + return "unknown"; +} + +const char* YGMeasureModeToString(const YGMeasureMode value) { + switch (value) { + case YGMeasureModeUndefined: + return "undefined"; + case YGMeasureModeExactly: + return "exactly"; + case YGMeasureModeAtMost: + return "at-most"; + } + return "unknown"; +} + +const char* YGNodeTypeToString(const YGNodeType value) { + switch (value) { + case YGNodeTypeDefault: + return "default"; + case YGNodeTypeText: + return "text"; + } + return "unknown"; +} + +const char* YGOverflowToString(const YGOverflow value) { + switch (value) { + case YGOverflowVisible: + return "visible"; + case YGOverflowHidden: + return "hidden"; + case YGOverflowScroll: + return "scroll"; + } + return "unknown"; +} + +const char* YGPositionTypeToString(const YGPositionType value) { + switch (value) { + case YGPositionTypeRelative: + return "relative"; + case YGPositionTypeAbsolute: + return "absolute"; + } + return "unknown"; +} + +const char* YGPrintOptionsToString(const YGPrintOptions value) { + switch (value) { + case YGPrintOptionsLayout: + return "layout"; + case YGPrintOptionsStyle: + return "style"; + case YGPrintOptionsChildren: + return "children"; + } + return "unknown"; +} + +const char* YGUnitToString(const YGUnit value) { + switch (value) { + case YGUnitUndefined: + return "undefined"; + case YGUnitPoint: + return "point"; + case YGUnitPercent: + return "percent"; + case YGUnitAuto: + return "auto"; + } + return "unknown"; +} + +const char* YGWrapToString(const YGWrap value) { + switch (value) { + case YGWrapNoWrap: + return "no-wrap"; + case YGWrapWrap: + return "wrap"; + case YGWrapWrapReverse: + return "wrap-reverse"; + } + return "unknown"; +} diff --git a/Sources/yoga/YGEnums.h b/Sources/yoga/YGEnums.h new file mode 100755 index 0000000..f06b0e0 --- /dev/null +++ b/Sources/yoga/YGEnums.h @@ -0,0 +1,150 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGMacros.h" + +#ifdef __cplusplus +namespace facebook { +namespace yoga { +namespace enums { + +template +constexpr int count(); // can't use `= delete` due to a defect in clang < 3.9 + +namespace detail { +template +constexpr int n() { + return sizeof...(xs); +} +} // namespace detail + +} // namespace enums +} // namespace yoga +} // namespace facebook +#endif + +#define YG_ENUM_DECL(NAME, ...) \ + typedef YG_ENUM_BEGIN(NAME){__VA_ARGS__} YG_ENUM_END(NAME); \ + WIN_EXPORT const char* NAME##ToString(NAME); + +#ifdef __cplusplus +#define YG_ENUM_SEQ_DECL(NAME, ...) \ + YG_ENUM_DECL(NAME, __VA_ARGS__) \ + YG_EXTERN_C_END \ + namespace facebook { \ + namespace yoga { \ + namespace enums { \ + template <> \ + constexpr int count() { \ + return detail::n<__VA_ARGS__>(); \ + } \ + } \ + } \ + } \ + YG_EXTERN_C_BEGIN +#else +#define YG_ENUM_SEQ_DECL YG_ENUM_DECL +#endif + +YG_EXTERN_C_BEGIN + +YG_ENUM_SEQ_DECL( + YGAlign, + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, + YGAlignBaseline, + YGAlignSpaceBetween, + YGAlignSpaceAround); + +YG_ENUM_SEQ_DECL(YGDimension, YGDimensionWidth, YGDimensionHeight) + +YG_ENUM_SEQ_DECL( + YGDirection, + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL) + +YG_ENUM_SEQ_DECL(YGDisplay, YGDisplayFlex, YGDisplayNone) + +YG_ENUM_SEQ_DECL( + YGEdge, + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll) + +YG_ENUM_SEQ_DECL(YGExperimentalFeature, YGExperimentalFeatureWebFlexBasis) + +YG_ENUM_SEQ_DECL( + YGFlexDirection, + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse) + +YG_ENUM_SEQ_DECL( + YGJustify, + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, + YGJustifySpaceEvenly) + +YG_ENUM_SEQ_DECL( + YGLogLevel, + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, + YGLogLevelFatal) + +YG_ENUM_SEQ_DECL( + YGMeasureMode, + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost) + +YG_ENUM_SEQ_DECL(YGNodeType, YGNodeTypeDefault, YGNodeTypeText) + +YG_ENUM_SEQ_DECL( + YGOverflow, + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll) + +YG_ENUM_SEQ_DECL(YGPositionType, YGPositionTypeRelative, YGPositionTypeAbsolute) + +YG_ENUM_DECL( + YGPrintOptions, + YGPrintOptionsLayout = 1, + YGPrintOptionsStyle = 2, + YGPrintOptionsChildren = 4) + +YG_ENUM_SEQ_DECL( + YGUnit, + YGUnitUndefined, + YGUnitPoint, + YGUnitPercent, + YGUnitAuto) + +YG_ENUM_SEQ_DECL(YGWrap, YGWrapNoWrap, YGWrapWrap, YGWrapWrapReverse) + +YG_EXTERN_C_END + +#undef YG_ENUM_DECL +#undef YG_ENUM_SEQ_DECL diff --git a/Sources/yoga/YGFloatOptional.h b/Sources/yoga/YGFloatOptional.h new file mode 100755 index 0000000..02d4c85 --- /dev/null +++ b/Sources/yoga/YGFloatOptional.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "Yoga-internal.h" + +struct YGFloatOptional { +private: + float value_ = std::numeric_limits::quiet_NaN(); + +public: + explicit constexpr YGFloatOptional(float value) : value_(value) {} + constexpr YGFloatOptional() = default; + + // returns the wrapped value, or a value x with YGIsUndefined(x) == true + constexpr float unwrap() const { + return value_; + } + + bool isUndefined() const { + return std::isnan(value_); + } + + YGFloatOptional operator+(YGFloatOptional op) const { + return YGFloatOptional{value_ + op.value_}; + } + bool operator>(YGFloatOptional op) const { + return value_ > op.value_; + } + bool operator<(YGFloatOptional op) const { + return value_ < op.value_; + } + bool operator>=(YGFloatOptional op) const { + return *this > op || *this == op; + } + bool operator<=(YGFloatOptional op) const { + return *this < op || *this == op; + } + bool operator==(YGFloatOptional op) const { + return value_ == op.value_ || (isUndefined() && op.isUndefined()); + } + bool operator!=(YGFloatOptional op) const { + return !(*this == op); + } + + bool operator==(float val) const { + return value_ == val || (isUndefined() && yoga::isUndefined(val)); + } + bool operator!=(float val) const { + return !(*this == val); + } +}; diff --git a/Sources/yoga/YGLayout.cpp b/Sources/yoga/YGLayout.cpp new file mode 100755 index 0000000..6f55d86 --- /dev/null +++ b/Sources/yoga/YGLayout.cpp @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGLayout.h" +#include "Utils.h" + +using namespace facebook; + +bool YGLayout::operator==(YGLayout layout) const { + bool isEqual = YGFloatArrayEqual(position, layout.position) && + YGFloatArrayEqual(dimensions, layout.dimensions) && + YGFloatArrayEqual(margin, layout.margin) && + YGFloatArrayEqual(border, layout.border) && + YGFloatArrayEqual(padding, layout.padding) && + direction == layout.direction && hadOverflow == layout.hadOverflow && + lastOwnerDirection == layout.lastOwnerDirection && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout && + computedFlexBasis == layout.computedFlexBasis; + + for (uint32_t i = 0; i < YG_MAX_CACHED_RESULT_COUNT && isEqual; ++i) { + isEqual = isEqual && cachedMeasurements[i] == layout.cachedMeasurements[i]; + } + + if (!yoga::isUndefined(measuredDimensions[0]) || + !yoga::isUndefined(layout.measuredDimensions[0])) { + isEqual = + isEqual && (measuredDimensions[0] == layout.measuredDimensions[0]); + } + if (!yoga::isUndefined(measuredDimensions[1]) || + !yoga::isUndefined(layout.measuredDimensions[1])) { + isEqual = + isEqual && (measuredDimensions[1] == layout.measuredDimensions[1]); + } + + return isEqual; +} diff --git a/Sources/yoga/YGLayout.h b/Sources/yoga/YGLayout.h new file mode 100755 index 0000000..4daa28e --- /dev/null +++ b/Sources/yoga/YGLayout.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGFloatOptional.h" +#include "Yoga-internal.h" + +constexpr std::array kYGDefaultDimensionValues = { + {YGUndefined, YGUndefined}}; + +struct YGLayout { + std::array position = {}; + std::array dimensions = kYGDefaultDimensionValues; + std::array margin = {}; + std::array border = {}; + std::array padding = {}; + YGDirection direction : 2; + bool didUseLegacyFlag : 1; + bool doesLegacyStretchFlagAffectsLayout : 1; + bool hadOverflow : 1; + + uint32_t computedFlexBasisGeneration = 0; + YGFloatOptional computedFlexBasis = {}; + + // Instead of recomputing the entire layout every single time, we cache some + // information to break early when nothing changed + uint32_t generationCount = 0; + YGDirection lastOwnerDirection = (YGDirection) -1; + + uint32_t nextCachedMeasurementsIndex = 0; + std::array + cachedMeasurements = {}; + std::array measuredDimensions = kYGDefaultDimensionValues; + + YGCachedMeasurement cachedLayout = YGCachedMeasurement(); + + YGLayout() + : direction(YGDirectionInherit), + didUseLegacyFlag(false), + doesLegacyStretchFlagAffectsLayout(false), + hadOverflow(false) {} + + bool operator==(YGLayout layout) const; + bool operator!=(YGLayout layout) const { + return !(*this == layout); + } +}; diff --git a/Sources/yoga/YGMacros.h b/Sources/yoga/YGMacros.h new file mode 100755 index 0000000..9c2989a --- /dev/null +++ b/Sources/yoga/YGMacros.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#pragma once + +#ifdef __cplusplus +#define YG_EXTERN_C_BEGIN extern "C" { +#define YG_EXTERN_C_END } +#else +#define YG_EXTERN_C_BEGIN +#define YG_EXTERN_C_END +#endif + +#ifdef _WINDLL +#define WIN_EXPORT __declspec(dllexport) +#else +#define WIN_EXPORT +#endif + +#ifdef NS_ENUM +// Cannot use NSInteger as NSInteger has a different size than int (which is the +// default type of a enum). Therefor when linking the Yoga C library into obj-c +// the header is a missmatch for the Yoga ABI. +#define YG_ENUM_BEGIN(name) NS_ENUM(int, name) +#define YG_ENUM_END(name) +#else +#define YG_ENUM_BEGIN(name) enum name +#define YG_ENUM_END(name) name +#endif diff --git a/Sources/yoga/YGMarker.cpp b/Sources/yoga/YGMarker.cpp new file mode 100755 index 0000000..21a8d1f --- /dev/null +++ b/Sources/yoga/YGMarker.cpp @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGMarker.h" +#include "YGConfig.h" + +void YGConfigSetMarkerCallbacks( + YGConfigRef config, + YGMarkerCallbacks markerCallbacks) { + config->markerCallbacks = markerCallbacks; +} diff --git a/Sources/yoga/YGMarker.h b/Sources/yoga/YGMarker.h new file mode 100755 index 0000000..25362ff --- /dev/null +++ b/Sources/yoga/YGMarker.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGMacros.h" + +YG_EXTERN_C_BEGIN + +typedef struct YGNode* YGNodeRef; +typedef struct YGConfig* YGConfigRef; + +typedef YG_ENUM_BEGIN(YGMarker){ + YGMarkerLayout, + YGMarkerMeasure, + YGMarkerBaselineFn, +} YG_ENUM_END(YGMarker); + +typedef struct { + int layouts; + int measures; + int maxMeasureCache; + int cachedLayouts; + int cachedMeasures; +} YGMarkerLayoutData; + +typedef struct { + bool _unused; +} YGMarkerNoData; + +typedef union { + YGMarkerLayoutData* layout; + YGMarkerNoData* noData; +} YGMarkerData; + +typedef struct { + // accepts marker type, a node ref, and marker data (depends on marker type) + // can return a handle or id that Yoga will pass to endMarker + void* (*startMarker)(YGMarker, YGNodeRef, YGMarkerData); + // accepts marker type, a node ref, marker data, and marker id as returned by + // startMarker + void (*endMarker)(YGMarker, YGNodeRef, YGMarkerData, void* id); +} YGMarkerCallbacks; + +void YGConfigSetMarkerCallbacks(YGConfigRef, YGMarkerCallbacks); + +YG_EXTERN_C_END + +#ifdef __cplusplus + +namespace facebook { +namespace yoga { +namespace marker { +namespace detail { + +template +struct MarkerData; + +template <> +struct MarkerData { + using type = YGMarkerLayoutData; + static type*& get(YGMarkerData& d) { + return d.layout; + } +}; + +struct NoMarkerData { + using type = YGMarkerNoData; + static type*& get(YGMarkerData& d) { + return d.noData; + } +}; + +template <> +struct MarkerData : NoMarkerData {}; + +template <> +struct MarkerData : NoMarkerData {}; + +} // namespace detail + +template +typename detail::MarkerData::type* data(YGMarkerData d) { + return detail::MarkerData::get(d); +} + +} // namespace marker +} // namespace yoga +} // namespace facebook + +#endif // __cplusplus diff --git a/Sources/yoga/YGNode.cpp b/Sources/yoga/YGNode.cpp new file mode 100755 index 0000000..2fb4510 --- /dev/null +++ b/Sources/yoga/YGNode.cpp @@ -0,0 +1,587 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include "CompactValue.h" +#include "Utils.h" + +using namespace facebook; +using facebook::yoga::detail::CompactValue; + +YGNode::YGNode(YGNode&& node) { + context_ = node.context_; + hasNewLayout_ = node.hasNewLayout_; + isReferenceBaseline_ = node.isReferenceBaseline_; + isDirty_ = node.isDirty_; + nodeType_ = node.nodeType_; + measureUsesContext_ = node.measureUsesContext_; + baselineUsesContext_ = node.baselineUsesContext_; + printUsesContext_ = node.printUsesContext_; + measure_ = node.measure_; + baseline_ = node.baseline_; + print_ = node.print_; + dirtied_ = node.dirtied_; + style_ = node.style_; + layout_ = node.layout_; + lineIndex_ = node.lineIndex_; + owner_ = node.owner_; + children_ = std::move(node.children_); + config_ = node.config_; + resolvedDimensions_ = node.resolvedDimensions_; + for (auto c : children_) { + c->setOwner(c); + } +} + +void YGNode::print(void* printContext) { + if (print_.noContext != nullptr) { + if (printUsesContext_) { + print_.withContext(this, printContext); + } else { + print_.noContext(this); + } + } +} + +YGFloatOptional YGNode::getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) const { + if (YGFlexDirectionIsRow(axis)) { + auto leadingPosition = YGComputedEdgeValue( + style_.position, YGEdgeStart, CompactValue::ofUndefined()); + if (!leadingPosition.isUndefined()) { + return YGResolveValue(leadingPosition, axisSize); + } + } + + auto leadingPosition = YGComputedEdgeValue( + style_.position, leading[axis], CompactValue::ofUndefined()); + + return leadingPosition.isUndefined() + ? YGFloatOptional{0} + : YGResolveValue(leadingPosition, axisSize); +} + +YGFloatOptional YGNode::getTrailingPosition( + const YGFlexDirection axis, + const float axisSize) const { + if (YGFlexDirectionIsRow(axis)) { + auto trailingPosition = YGComputedEdgeValue( + style_.position, YGEdgeEnd, CompactValue::ofUndefined()); + if (!trailingPosition.isUndefined()) { + return YGResolveValue(trailingPosition, axisSize); + } + } + + auto trailingPosition = YGComputedEdgeValue( + style_.position, trailing[axis], CompactValue::ofUndefined()); + + return trailingPosition.isUndefined() + ? YGFloatOptional{0} + : YGResolveValue(trailingPosition, axisSize); +} + +bool YGNode::isLeadingPositionDefined(const YGFlexDirection axis) const { + return (YGFlexDirectionIsRow(axis) && + !YGComputedEdgeValue( + style_.position, YGEdgeStart, CompactValue::ofUndefined()) + .isUndefined()) || + !YGComputedEdgeValue( + style_.position, leading[axis], CompactValue::ofUndefined()) + .isUndefined(); +} + +bool YGNode::isTrailingPosDefined(const YGFlexDirection axis) const { + return (YGFlexDirectionIsRow(axis) && + !YGComputedEdgeValue( + style_.position, YGEdgeEnd, CompactValue::ofUndefined()) + .isUndefined()) || + !YGComputedEdgeValue( + style_.position, trailing[axis], CompactValue::ofUndefined()) + .isUndefined(); +} + +YGFloatOptional YGNode::getLeadingMargin( + const YGFlexDirection axis, + const float widthSize) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin[YGEdgeStart].isUndefined()) { + return YGResolveValueMargin(style_.margin[YGEdgeStart], widthSize); + } + + return YGResolveValueMargin( + YGComputedEdgeValue(style_.margin, leading[axis], CompactValue::ofZero()), + widthSize); +} + +YGFloatOptional YGNode::getTrailingMargin( + const YGFlexDirection axis, + const float widthSize) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin[YGEdgeEnd].isUndefined()) { + return YGResolveValueMargin(style_.margin[YGEdgeEnd], widthSize); + } + + return YGResolveValueMargin( + YGComputedEdgeValue( + style_.margin, trailing[axis], CompactValue::ofZero()), + widthSize); +} + +YGFloatOptional YGNode::getMarginForAxis( + const YGFlexDirection axis, + const float widthSize) const { + return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize); +} + +YGSize YGNode::measure( + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode, + void* layoutContext) { + + return measureUsesContext_ + ? measure_.withContext( + this, width, widthMode, height, heightMode, layoutContext) + : measure_.noContext(this, width, widthMode, height, heightMode); +} + +float YGNode::baseline(float width, float height, void* layoutContext) { + return baselineUsesContext_ + ? baseline_.withContext(this, width, height, layoutContext) + : baseline_.noContext(this, width, height); +} + +// Setters + +void YGNode::setMeasureFunc(decltype(YGNode::measure_) measureFunc) { + if (measureFunc.noContext == 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."); + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(YGNodeTypeText); + } + + measure_ = measureFunc; +} + +void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { + measureUsesContext_ = false; + decltype(YGNode::measure_) m; + m.noContext = measureFunc; + setMeasureFunc(m); +} + +void YGNode::setMeasureFunc(MeasureWithContextFn measureFunc) { + measureUsesContext_ = true; + decltype(YGNode::measure_) m; + m.withContext = measureFunc; + setMeasureFunc(m); +} + +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::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::setLayoutLastOwnerDirection(YGDirection direction) { + layout_.lastOwnerDirection = direction; +} + +void YGNode::setLayoutComputedFlexBasis( + const YGFloatOptional 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. +YGFloatOptional YGNode::relativePosition( + const YGFlexDirection axis, + const float axisSize) const { + if (isLeadingPositionDefined(axis)) { + return getLeadingPosition(axis, axisSize); + } + + YGFloatOptional trailingPosition = getTrailingPosition(axis, axisSize); + if (!trailingPosition.isUndefined()) { + trailingPosition = YGFloatOptional{-1 * trailingPosition.unwrap()}; + } + return trailingPosition; +} + +void YGNode::setPosition( + const YGDirection direction, + const float mainSize, + const float crossSize, + const float ownerWidth) { + /* Root nodes should be always layouted as LTR, so we don't return negative + * values. */ + const YGDirection directionRespectingRoot = + owner_ != nullptr ? direction : YGDirectionLTR; + const YGFlexDirection mainAxis = + YGResolveFlexDirection(style_.flexDirection, directionRespectingRoot); + const YGFlexDirection crossAxis = + YGFlexDirectionCross(mainAxis, directionRespectingRoot); + + const YGFloatOptional relativePositionMain = + relativePosition(mainAxis, mainSize); + const YGFloatOptional relativePositionCross = + relativePosition(crossAxis, crossSize); + + setLayoutPosition( + (getLeadingMargin(mainAxis, ownerWidth) + relativePositionMain).unwrap(), + leading[mainAxis]); + setLayoutPosition( + (getTrailingMargin(mainAxis, ownerWidth) + relativePositionMain).unwrap(), + trailing[mainAxis]); + setLayoutPosition( + (getLeadingMargin(crossAxis, ownerWidth) + relativePositionCross) + .unwrap(), + leading[crossAxis]); + setLayoutPosition( + (getTrailingMargin(crossAxis, ownerWidth) + relativePositionCross) + .unwrap(), + trailing[crossAxis]); +} + +YGValue YGNode::marginLeadingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin[YGEdgeStart].isUndefined()) { + return style_.margin[YGEdgeStart]; + } else { + return style_.margin[leading[axis]]; + } +} + +YGValue YGNode::marginTrailingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin[YGEdgeEnd].isUndefined()) { + 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 (!style_.flex.isUndefined() && style_.flex.unwrap() > 0.0f) { + return config_->useWebDefaults ? YGValueAuto : YGValueZero; + } + return YGValueAuto; +} + +void YGNode::resolveDimension() { + using namespace yoga; + for (int dim = YGDimensionWidth; dim < enums::count(); dim++) { + if (!getStyle().maxDimensions[dim].isUndefined() && + YGValueEqual( + getStyle().maxDimensions[dim], style_.minDimensions[dim])) { + resolvedDimensions_[dim] = style_.maxDimensions[dim]; + } else { + resolvedDimensions_[dim] = style_.dimensions[dim]; + } + } +} + +YGDirection YGNode::resolveDirection(const YGDirection ownerDirection) { + if (style_.direction == YGDirectionInherit) { + return ownerDirection > YGDirectionInherit ? ownerDirection + : YGDirectionLTR; + } else { + return style_.direction; + } +} + +void YGNode::clearChildren() { + children_.clear(); + children_.shrink_to_fit(); +} + +// Other Methods + +void YGNode::cloneChildrenIfNeeded(void* cloneContext) { + iterChildrenAfterCloningIfNeeded([](YGNodeRef, void*) {}, cloneContext); +} + +void YGNode::markDirtyAndPropogate() { + if (!isDirty_) { + setDirty(true); + setLayoutComputedFlexBasis(YGFloatOptional()); + if (owner_) { + owner_->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 (owner_ == nullptr) { + return 0.0; + } + if (!style_.flexGrow.isUndefined()) { + return style_.flexGrow.unwrap(); + } + if (!style_.flex.isUndefined() && style_.flex.unwrap() > 0.0f) { + return style_.flex.unwrap(); + } + return kDefaultFlexGrow; +} + +float YGNode::resolveFlexShrink() { + if (owner_ == nullptr) { + return 0.0; + } + if (!style_.flexShrink.isUndefined()) { + return style_.flexShrink.unwrap(); + } + if (!config_->useWebDefaults && !style_.flex.isUndefined() && + style_.flex.unwrap() < 0.0f) { + return -style_.flex.unwrap(); + } + return config_->useWebDefaults ? kWebDefaultFlexShrink : kDefaultFlexShrink; +} + +bool YGNode::isNodeFlexible() { + return ( + (style_.positionType == YGPositionTypeRelative) && + (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +} + +float YGNode::getLeadingBorder(const YGFlexDirection axis) const { + YGValue leadingBorder; + if (YGFlexDirectionIsRow(axis) && !style_.border[YGEdgeStart].isUndefined()) { + leadingBorder = style_.border[YGEdgeStart]; + if (leadingBorder.value >= 0) { + return leadingBorder.value; + } + } + + leadingBorder = + YGComputedEdgeValue(style_.border, leading[axis], CompactValue::ofZero()); + return YGFloatMax(leadingBorder.value, 0.0f); +} + +float YGNode::getTrailingBorder(const YGFlexDirection flexDirection) const { + YGValue trailingBorder; + if (YGFlexDirectionIsRow(flexDirection) && + !style_.border[YGEdgeEnd].isUndefined()) { + trailingBorder = style_.border[YGEdgeEnd]; + if (trailingBorder.value >= 0.0f) { + return trailingBorder.value; + } + } + + trailingBorder = YGComputedEdgeValue( + style_.border, trailing[flexDirection], CompactValue::ofZero()); + return YGFloatMax(trailingBorder.value, 0.0f); +} + +YGFloatOptional YGNode::getLeadingPadding( + const YGFlexDirection axis, + const float widthSize) const { + const YGFloatOptional paddingEdgeStart = + YGResolveValue(style_.padding[YGEdgeStart], widthSize); + if (YGFlexDirectionIsRow(axis) && + !style_.padding[YGEdgeStart].isUndefined() && + !paddingEdgeStart.isUndefined() && paddingEdgeStart.unwrap() >= 0.0f) { + return paddingEdgeStart; + } + + YGFloatOptional resolvedValue = YGResolveValue( + YGComputedEdgeValue( + style_.padding, leading[axis], CompactValue::ofZero()), + widthSize); + return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); +} + +YGFloatOptional YGNode::getTrailingPadding( + const YGFlexDirection axis, + const float widthSize) const { + const YGFloatOptional paddingEdgeEnd = + YGResolveValue(style_.padding[YGEdgeEnd], widthSize); + if (YGFlexDirectionIsRow(axis) && paddingEdgeEnd >= YGFloatOptional{0.0f}) { + return paddingEdgeEnd; + } + + YGFloatOptional resolvedValue = YGResolveValue( + YGComputedEdgeValue( + style_.padding, trailing[axis], CompactValue::ofZero()), + widthSize); + + return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); +} + +YGFloatOptional YGNode::getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const { + return getLeadingPadding(axis, widthSize) + + YGFloatOptional(getLeadingBorder(axis)); +} + +YGFloatOptional YGNode::getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const { + return getTrailingPadding(axis, widthSize) + + YGFloatOptional(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; +} + +void YGNode::reset() { + YGAssertWithNode( + this, + children_.size() == 0, + "Cannot reset a node which still has children attached"); + YGAssertWithNode( + this, owner_ == nullptr, "Cannot reset a node still attached to a owner"); + + clearChildren(); + + auto config = getConfig(); + *this = YGNode{}; + if (config->useWebDefaults) { + setStyleFlexDirection(YGFlexDirectionRow); + setStyleAlignContent(YGAlignStretch); + } + setConfig(config); +} diff --git a/Sources/yoga/YGNode.h b/Sources/yoga/YGNode.h new file mode 100755 index 0000000..b5ff98a --- /dev/null +++ b/Sources/yoga/YGNode.h @@ -0,0 +1,361 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGConfig.h" +#include "YGLayout.h" +#include "YGStyle.h" +#include "Yoga-internal.h" + +struct YGNode { + using MeasureWithContextFn = + YGSize (*)(YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*); + using BaselineWithContextFn = float (*)(YGNode*, float, float, void*); + using PrintWithContextFn = void (*)(YGNode*, void*); + +private: + void* context_ = nullptr; + bool hasNewLayout_ : 1; + bool isReferenceBaseline_ : 1; + bool isDirty_ : 1; + YGNodeType nodeType_ : 1; + bool measureUsesContext_ : 1; + bool baselineUsesContext_ : 1; + bool printUsesContext_ : 1; + union { + YGMeasureFunc noContext; + MeasureWithContextFn withContext; + } measure_ = {nullptr}; + union { + YGBaselineFunc noContext; + BaselineWithContextFn withContext; + } baseline_ = {nullptr}; + union { + YGPrintFunc noContext; + PrintWithContextFn withContext; + } print_ = {nullptr}; + YGDirtiedFunc dirtied_ = nullptr; + YGStyle style_ = {}; + YGLayout layout_ = {}; + uint32_t lineIndex_ = 0; + YGNodeRef owner_ = nullptr; + YGVector children_ = {}; + YGConfigRef config_ = nullptr; + std::array resolvedDimensions_ = { + {YGValueUndefined, YGValueUndefined}}; + + YGFloatOptional relativePosition( + const YGFlexDirection axis, + const float axisSize) const; + + void setMeasureFunc(decltype(measure_)); + void setBaselineFunc(decltype(baseline_)); + + // DANGER DANGER DANGER! + // If the the node assigned to has children, we'd either have to deallocate + // them (potentially incorrect) or ignore them (danger of leaks). Only ever + // use this after checking that there are no children. + // DO NOT CHANGE THE VISIBILITY OF THIS METHOD! + YGNode& operator=(YGNode&&) = default; + +public: + YGNode() + : hasNewLayout_{true}, + isReferenceBaseline_{false}, + isDirty_{false}, + nodeType_{YGNodeTypeDefault}, + measureUsesContext_{false}, + baselineUsesContext_{false}, + printUsesContext_{false} {} + ~YGNode() = default; // cleanup of owner/children relationships in YGNodeFree + explicit YGNode(const YGConfigRef newConfig) : config_(newConfig){}; + + YGNode(YGNode&&); + + // Does not expose true value semantics, as children are not cloned eagerly. + // Should we remove this? + YGNode(const YGNode& node) = default; + + // assignment means potential leaks of existing children, or alternatively + // freeing unowned memory, double free, or freeing stack memory. + YGNode& operator=(const YGNode&) = delete; + + // Getters + void* getContext() const { + return context_; + } + + void print(void*); + + bool getHasNewLayout() const { + return hasNewLayout_; + } + + YGNodeType getNodeType() const { + return nodeType_; + } + + bool hasMeasureFunc() const noexcept { + return measure_.noContext != nullptr; + } + + YGSize measure(float, YGMeasureMode, float, YGMeasureMode, void*); + + bool hasBaselineFunc() const noexcept { + return baseline_.noContext != nullptr; + } + + float baseline(float width, float height, void* layoutContext); + + YGDirtiedFunc getDirtied() const { + return dirtied_; + } + + // For Performance reasons passing as reference. + YGStyle& getStyle() { + return style_; + } + + const YGStyle& getStyle() const { + return style_; + } + + // For Performance reasons passing as reference. + YGLayout& getLayout() { + return layout_; + } + + const YGLayout& getLayout() const { + return layout_; + } + + uint32_t getLineIndex() const { + return lineIndex_; + } + + bool isReferenceBaseline() { + return isReferenceBaseline_; + } + + // returns the YGNodeRef that owns this YGNode. An owner is used to identify + // the YogaTree that a YGNode belongs to. This method will return the parent + // of the YGNode when a YGNode only belongs to one YogaTree or nullptr when + // the YGNode is shared between two or more YogaTrees. + YGNodeRef getOwner() const { + return owner_; + } + + // Deprecated, use getOwner() instead. + YGNodeRef getParent() const { + return getOwner(); + } + + const YGVector& getChildren() const { + return children_; + } + + // Applies a callback to all children, after cloning them if they are not + // owned. + template + void iterChildrenAfterCloningIfNeeded(T callback, void* cloneContext) { + int i = 0; + for (YGNodeRef& child : children_) { + if (child->getOwner() != this) { + child = config_->cloneNode(child, this, i, cloneContext); + child->setOwner(this); + } + i += 1; + + callback(child, cloneContext); + } + } + + YGNodeRef getChild(uint32_t index) const { + return children_.at(index); + } + + YGConfigRef getConfig() const { + return config_; + } + + bool isDirty() const { + return isDirty_; + } + + std::array getResolvedDimensions() const { + return resolvedDimensions_; + } + + YGValue getResolvedDimension(int index) const { + return resolvedDimensions_[index]; + } + + // Methods related to positions, margin, padding and border + YGFloatOptional getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) const; + bool isLeadingPositionDefined(const YGFlexDirection axis) const; + bool isTrailingPosDefined(const YGFlexDirection axis) const; + YGFloatOptional getTrailingPosition( + const YGFlexDirection axis, + const float axisSize) const; + YGFloatOptional getLeadingMargin( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingMargin( + const YGFlexDirection axis, + const float widthSize) const; + float getLeadingBorder(const YGFlexDirection flexDirection) const; + float getTrailingBorder(const YGFlexDirection flexDirection) const; + YGFloatOptional getLeadingPadding( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingPadding( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getMarginForAxis( + const YGFlexDirection axis, + const float widthSize) const; + // Setters + + void setContext(void* context) { + context_ = context; + } + + void setPrintFunc(YGPrintFunc printFunc) { + print_.noContext = printFunc; + printUsesContext_ = false; + } + void setPrintFunc(PrintWithContextFn printFunc) { + print_.withContext = printFunc; + printUsesContext_ = true; + } + void setPrintFunc(std::nullptr_t) { + setPrintFunc(YGPrintFunc{nullptr}); + } + + void setHasNewLayout(bool hasNewLayout) { + hasNewLayout_ = hasNewLayout; + } + + void setNodeType(YGNodeType nodeType) { + nodeType_ = nodeType; + } + + void setStyleFlexDirection(YGFlexDirection direction) { + style_.flexDirection = direction; + } + + void setStyleAlignContent(YGAlign alignContent) { + style_.alignContent = alignContent; + } + + void setMeasureFunc(YGMeasureFunc measureFunc); + void setMeasureFunc(MeasureWithContextFn); + void setMeasureFunc(std::nullptr_t) { + return setMeasureFunc(YGMeasureFunc{nullptr}); + } + + void setBaselineFunc(YGBaselineFunc baseLineFunc) { + baselineUsesContext_ = false; + baseline_.noContext = baseLineFunc; + } + void setBaselineFunc(BaselineWithContextFn baseLineFunc) { + baselineUsesContext_ = true; + baseline_.withContext = baseLineFunc; + } + void setBaselineFunc(std::nullptr_t) { + return setBaselineFunc(YGBaselineFunc{nullptr}); + } + + void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { + dirtied_ = dirtiedFunc; + } + + void setStyle(const YGStyle& style) { + style_ = style; + } + + void setLayout(const YGLayout& layout) { + layout_ = layout; + } + + void setLineIndex(uint32_t lineIndex) { + lineIndex_ = lineIndex; + } + + void setIsReferenceBaseline(bool isReferenceBaseline) { + isReferenceBaseline_ = isReferenceBaseline; + } + + void setOwner(YGNodeRef owner) { + owner_ = owner; + } + + void setChildren(const YGVector& children) { + children_ = children; + } + + // TODO: rvalue override for setChildren + + void setConfig(YGConfigRef config) { + config_ = config; + } + + void setDirty(bool isDirty); + void setLayoutLastOwnerDirection(YGDirection direction); + void setLayoutComputedFlexBasis(const YGFloatOptional 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 ownerWidth); + 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 ownerDirection); + 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*); + void markDirtyAndPropogate(); + float resolveFlexGrow(); + float resolveFlexShrink(); + bool isNodeFlexible(); + bool didUseLegacyFlag(); + bool isLayoutTreeEqualToNode(const YGNode& node) const; + void reset(); +}; diff --git a/Sources/yoga/YGNodePrint.cpp b/Sources/yoga/YGNodePrint.cpp new file mode 100755 index 0000000..cc3ead7 --- /dev/null +++ b/Sources/yoga/YGNodePrint.cpp @@ -0,0 +1,235 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#ifdef DEBUG +#include "YGNodePrint.h" +#include +#include "YGEnums.h" +#include "YGNode.h" +#include "Yoga-internal.h" +#include "Utils.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 YGStyle::Edges& 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 appendFloatOptionalIfDefined( + string& base, + const string key, + const YGFloatOptional num) { + if (!num.isUndefined()) { + appendFormatedString(base, "%s: %g; ", key.c_str(), num.unwrap()); + } +} + +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 (number.unit == YGUnitAuto) { + base.append(str + ": auto; "); + } else if (!YGFloatsEqual(number.value, 0)) { + appendNumberIfNotUndefined(base, str, number); + } +} + +static void appendEdges( + string& base, + const string& key, + const YGStyle::Edges& 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 YGStyle::Edges& edges, + const YGEdge edge) { + appendNumberIfNotUndefined( + base, + str, + YGComputedEdgeValue(edges, edge, detail::CompactValue::ofUndefined())); +} + +void YGNodeToString( + std::string& str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level) { + indent(str, level); + appendFormatedString(str, "
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)); + } + appendFloatOptionalIfDefined(str, "flex-grow", node->getStyle().flexGrow); + appendFloatOptionalIfDefined( + str, "flex-shrink", node->getStyle().flexShrink); + appendNumberIfNotAuto(str, "flex-basis", node->getStyle().flexBasis); + appendFloatOptionalIfDefined(str, "flex", node->getStyle().flex); + + if (node->getStyle().flexWrap != YGNode().getStyle().flexWrap) { + appendFormatedString( + str, "flex-wrap: %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->hasMeasureFunc()) { + 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 +#endif diff --git a/Sources/yoga/YGNodePrint.h b/Sources/yoga/YGNodePrint.h new file mode 100755 index 0000000..13cf367 --- /dev/null +++ b/Sources/yoga/YGNodePrint.h @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#ifdef DEBUG +#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 +#endif diff --git a/Sources/yoga/YGStyle.cpp b/Sources/yoga/YGStyle.cpp new file mode 100755 index 0000000..26b0f26 --- /dev/null +++ b/Sources/yoga/YGStyle.cpp @@ -0,0 +1,52 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGStyle.h" +#include "Utils.h" + +// Yoga specific properties, not compatible with flexbox specification +bool operator==(const YGStyle& lhs, const YGStyle& rhs) { + bool areNonFloatValuesEqual = lhs.direction == rhs.direction && + lhs.flexDirection == rhs.flexDirection && + lhs.justifyContent == rhs.justifyContent && + lhs.alignContent == rhs.alignContent && + lhs.alignItems == rhs.alignItems && lhs.alignSelf == rhs.alignSelf && + lhs.positionType == rhs.positionType && lhs.flexWrap == rhs.flexWrap && + lhs.overflow == rhs.overflow && lhs.display == rhs.display && + YGValueEqual(lhs.flexBasis, rhs.flexBasis) && lhs.margin == rhs.margin && + lhs.position == rhs.position && lhs.padding == rhs.padding && + lhs.border == rhs.border && lhs.dimensions == rhs.dimensions && + lhs.minDimensions == rhs.minDimensions && + lhs.maxDimensions == rhs.maxDimensions; + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flex.isUndefined() == rhs.flex.isUndefined(); + if (areNonFloatValuesEqual && !lhs.flex.isUndefined() && + !rhs.flex.isUndefined()) { + areNonFloatValuesEqual = areNonFloatValuesEqual && lhs.flex == rhs.flex; + } + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flexGrow.isUndefined() == rhs.flexGrow.isUndefined(); + if (areNonFloatValuesEqual && !lhs.flexGrow.isUndefined()) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.flexGrow == rhs.flexGrow; + } + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flexShrink.isUndefined() == rhs.flexShrink.isUndefined(); + if (areNonFloatValuesEqual && !rhs.flexShrink.isUndefined()) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.flexShrink == rhs.flexShrink; + } + + if (!(lhs.aspectRatio.isUndefined() && rhs.aspectRatio.isUndefined())) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.aspectRatio == rhs.aspectRatio; + } + + return areNonFloatValuesEqual; +} diff --git a/Sources/yoga/YGStyle.h b/Sources/yoga/YGStyle.h new file mode 100755 index 0000000..7e93502 --- /dev/null +++ b/Sources/yoga/YGStyle.h @@ -0,0 +1,75 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "CompactValue.h" +#include "YGEnums.h" +#include "YGFloatOptional.h" +#include "Yoga-internal.h" +#include "Yoga.h" + +#if !defined(ENUM_BITFIELDS_NOT_SUPPORTED) +#define BITFIELD_ENUM_SIZED(num) : num +#else +#define BITFIELD_ENUM_SIZED(num) +#endif + +struct YGStyle { +private: + using CompactValue = facebook::yoga::detail::CompactValue; + +public: + using Dimensions = facebook::yoga::detail::Values<2>; + using Edges = + facebook::yoga::detail::Values()>; + + /* Some platforms don't support enum bitfields, + so please use BITFIELD_ENUM_SIZED(BITS_COUNT) */ + YGDirection direction BITFIELD_ENUM_SIZED(2); + YGFlexDirection flexDirection BITFIELD_ENUM_SIZED(2); + YGJustify justifyContent BITFIELD_ENUM_SIZED(3); + YGAlign alignContent BITFIELD_ENUM_SIZED(3); + YGAlign alignItems BITFIELD_ENUM_SIZED(3); + YGAlign alignSelf BITFIELD_ENUM_SIZED(3); + YGPositionType positionType BITFIELD_ENUM_SIZED(1); + YGWrap flexWrap BITFIELD_ENUM_SIZED(2); + YGOverflow overflow BITFIELD_ENUM_SIZED(2); + YGDisplay display BITFIELD_ENUM_SIZED(1); + YGFloatOptional flex = {}; + YGFloatOptional flexGrow = {}; + YGFloatOptional flexShrink = {}; + CompactValue flexBasis = CompactValue::ofAuto(); + Edges margin = {}; + Edges position = {}; + Edges padding = {}; + Edges border = {}; + Dimensions dimensions{CompactValue::ofAuto()}; + Dimensions minDimensions = {}; + Dimensions maxDimensions = {}; + // Yoga specific properties, not compatible with flexbox specification + YGFloatOptional aspectRatio = {}; + + YGStyle() + : direction(YGDirectionInherit), + flexDirection(YGFlexDirectionColumn), + justifyContent(YGJustifyFlexStart), + alignContent(YGAlignFlexStart), + alignItems(YGAlignStretch), + alignSelf(YGAlignAuto), + positionType(YGPositionTypeRelative), + flexWrap(YGWrapNoWrap), + overflow(YGOverflowVisible), + display(YGDisplayFlex) {} + ~YGStyle() = default; +}; + +bool operator==(const YGStyle& lhs, const YGStyle& rhs); +inline bool operator!=(const YGStyle& lhs, const YGStyle& rhs) { + return !(lhs == rhs); +} diff --git a/Sources/yoga/YGValue.cpp b/Sources/yoga/YGValue.cpp new file mode 100755 index 0000000..fcdd0c6 --- /dev/null +++ b/Sources/yoga/YGValue.cpp @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGValue.h" + +const YGValue YGValueZero = {0, YGUnitPoint}; +const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; +const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; diff --git a/Sources/yoga/YGValue.h b/Sources/yoga/YGValue.h new file mode 100755 index 0000000..170047e --- /dev/null +++ b/Sources/yoga/YGValue.h @@ -0,0 +1,83 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGEnums.h" +#include "YGMacros.h" + +YG_EXTERN_C_BEGIN + +// Not defined in MSVC++ +#ifndef NAN +static const uint32_t __nan = 0x7fc00000; +#define NAN (*(const float*) __nan) +#endif + +#define YGUndefined NAN + +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + +extern const YGValue YGValueAuto; +extern const YGValue YGValueUndefined; +extern const YGValue YGValueZero; + +YG_EXTERN_C_END + +#ifdef __cplusplus + +inline bool operator==(const YGValue& lhs, const YGValue& rhs) { + if (lhs.unit != rhs.unit) { + return false; + } + + switch (lhs.unit) { + case YGUnitUndefined: + case YGUnitAuto: + return true; + case YGUnitPoint: + case YGUnitPercent: + return lhs.value == rhs.value; + } + + return false; +} + +inline bool operator!=(const YGValue& lhs, const YGValue& rhs) { + return !(lhs == rhs); +} + +inline YGValue operator-(const YGValue& value) { + return {-value.value, value.unit}; +} + +namespace facebook { +namespace yoga { +namespace literals { + +inline YGValue operator"" _pt(long double value) { + return YGValue{static_cast(value), YGUnitPoint}; +} +inline YGValue operator"" _pt(unsigned long long value) { + return operator"" _pt(static_cast(value)); +} + +inline YGValue operator"" _percent(long double value) { + return YGValue{static_cast(value), YGUnitPercent}; +} +inline YGValue operator"" _percent(unsigned long long value) { + return operator"" _percent(static_cast(value)); +} + +} // namespace literals +} // namespace yoga +} // namespace facebook + +#endif diff --git a/Sources/yoga/Yoga-internal.h b/Sources/yoga/Yoga-internal.h new file mode 100755 index 0000000..be81592 --- /dev/null +++ b/Sources/yoga/Yoga-internal.h @@ -0,0 +1,162 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "CompactValue.h" +#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); + +void YGNodeCalculateLayoutWithContext( + YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection ownerDirection, + void* layoutContext); + +void YGSetUsedCachedEntries(size_t); + +YG_EXTERN_C_END + +namespace facebook { +namespace yoga { + +inline bool isUndefined(float value) { + return std::isnan(value); +} + +} // namespace yoga +} // namespace facebook + +using namespace facebook; + +extern const std::array trailing; +extern const std::array leading; +extern const YGValue YGValueUndefined; +extern const YGValue YGValueAuto; +extern const YGValue YGValueZero; + +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 (!yoga::isUndefined(availableWidth) || + !yoga::isUndefined(measurement.availableWidth)) { + isEqual = isEqual && availableWidth == measurement.availableWidth; + } + if (!yoga::isUndefined(availableHeight) || + !yoga::isUndefined(measurement.availableHeight)) { + isEqual = isEqual && availableHeight == measurement.availableHeight; + } + if (!yoga::isUndefined(computedWidth) || + !yoga::isUndefined(measurement.computedWidth)) { + isEqual = isEqual && computedWidth == measurement.computedWidth; + } + if (!yoga::isUndefined(computedHeight) || + !yoga::isUndefined(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 + +namespace facebook { +namespace yoga { +namespace detail { + +template +class Values { +private: + std::array values_; + +public: + Values() = default; + explicit Values(const YGValue& defaultValue) noexcept { + values_.fill(defaultValue); + } + + const CompactValue& operator[](size_t i) const noexcept { + return values_[i]; + } + CompactValue& operator[](size_t i) noexcept { + return values_[i]; + } + + template + YGValue get() const noexcept { + return std::get(values_); + } + + template + void set(YGValue& value) noexcept { + std::get(values_) = value; + } + + template + void set(YGValue&& value) noexcept { + set(value); + } + + bool operator==(const Values& other) const noexcept { + for (size_t i = 0; i < Size; ++i) { + if (values_[i] != other.values_[i]) { + return false; + } + } + return true; + } + + Values& operator=(const Values& other) = default; +}; + +} // namespace detail +} // namespace yoga +} // namespace facebook + +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 facebook::yoga::detail::CompactValue YGComputedEdgeValue( + const facebook::yoga::detail::Values< + facebook::yoga::enums::count()>& edges, + YGEdge edge, + facebook::yoga::detail::CompactValue defaultValue); diff --git a/Sources/yoga/Yoga.cpp b/Sources/yoga/Yoga.cpp new file mode 100755 index 0000000..431a59a --- /dev/null +++ b/Sources/yoga/Yoga.cpp @@ -0,0 +1,4285 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "log.h" +#include +#include +#include +#include "Utils.h" +#include "YGNode.h" +#include "YGNodePrint.h" +#include "Yoga-internal.h" +#include "instrumentation.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 + +using namespace facebook::yoga; +using detail::Log; + +namespace { +size_t usedMeasureCacheEntries = YG_MAX_CACHED_RESULT_COUNT; +} + +void YGSetUsedCachedEntries(size_t n) { + usedMeasureCacheEntries = + n == 0 || n > YG_MAX_CACHED_RESULT_COUNT ? YG_MAX_CACHED_RESULT_COUNT : n; +} + +#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 + +#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 facebook::yoga::isUndefined(value); +} + +detail::CompactValue YGComputedEdgeValue( + const YGStyle::Edges& edges, + YGEdge edge, + detail::CompactValue defaultValue) { + if (!edges[edge].isUndefined()) { + return edges[edge]; + } + + if ((edge == YGEdgeTop || edge == YGEdgeBottom) && + !edges[YGEdgeVertical].isUndefined()) { + return edges[YGEdgeVertical]; + } + + if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || + edge == YGEdgeEnd) && + !edges[YGEdgeHorizontal].isUndefined()) { + return edges[YGEdgeHorizontal]; + } + + if (!edges[YGEdgeAll].isUndefined()) { + return edges[YGEdgeAll]; + } + + if (edge == YGEdgeStart || edge == YGEdgeEnd) { + return detail::CompactValue::ofUndefined(); + } + + return defaultValue; +} + +void* YGNodeGetContext(YGNodeRef node) { + return node->getContext(); +} + +void YGNodeSetContext(YGNodeRef node, void* context) { + return node->setContext(context); +} + +bool YGNodeHasMeasureFunc(YGNodeRef node) { + return node->hasMeasureFunc(); +} + +void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) { + node->setMeasureFunc(measureFunc); +} + +bool YGNodeHasBaselineFunc(YGNodeRef node) { + return node->hasBaselineFunc(); +} + +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); +} + +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) { + node->setPrintFunc(printFunc); +} + +bool YGNodeGetHasNewLayout(YGNodeRef node) { + return node->getHasNewLayout(); +} + +void YGConfigSetPrintTreeFlag(YGConfigRef config, bool enabled) { + config->printTree = enabled; +} + +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; +} + +YGConfigRef YGConfigGetDefault() { + static YGConfigRef defaultConfig = YGConfigNew(); + return defaultConfig; +} + +YGNodeRef YGNodeNew(void) { + return YGNodeNewWithConfig(YGConfigGetDefault()); +} + +YGNodeRef YGNodeClone(YGNodeRef oldNode) { + YGNodeRef node = new YGNode(*oldNode); + YGAssertWithConfig( + oldNode->getConfig(), + node != nullptr, + "Could not allocate memory for node"); + gNodeInstanceCount++; + node->setOwner(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->setOwner(node); + vec.push_back(childNode); + } + node->setChildren(vec); + + if (oldNode->getConfig() != nullptr) { + node->setConfig(YGConfigClone(*(oldNode->getConfig()))); + } + + return node; +} + +void YGNodeFree(const YGNodeRef node) { + if (YGNodeRef owner = node->getOwner()) { + owner->removeChild(node); + node->setOwner(nullptr); + } + + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + child->setOwner(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 (auto* child : root->getChildren()) { + YGConfigFreeRecursive(child); + } +} + +void YGNodeFreeRecursiveWithCleanupFunc( + const YGNodeRef root, + YGNodeCleanupFunc cleanup) { + uint32_t skipped = 0; + while (YGNodeGetChildCount(root) > skipped) { + const YGNodeRef child = YGNodeGetChild(root, skipped); + if (child->getOwner() != root) { + // Don't free shared nodes that we don't own. + skipped += 1; + } else { + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } + } + if (cleanup != nullptr) { + cleanup(root); + } + YGNodeFree(root); +} + +void YGNodeFreeRecursive(const YGNodeRef root) { + return YGNodeFreeRecursiveWithCleanupFunc(root, nullptr); +} + +void YGNodeReset(YGNodeRef node) { + node->reset(); +} + +int32_t YGNodeGetInstanceCount(void) { + return gNodeInstanceCount; +} + +int32_t YGConfigGetInstanceCount(void) { + return gConfigInstanceCount; +} + +YGConfigRef YGConfigNew(void) { +#ifdef ANDROID + const YGConfigRef config = new YGConfig(YGAndroidLog); +#else + const YGConfigRef config = new YGConfig(YGDefaultLog); +#endif + gConfigInstanceCount++; + return config; +} + +void YGConfigFree(const YGConfigRef config) { + delete config; + gConfigInstanceCount--; +} + +void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) { + memcpy(dest, src, sizeof(YGConfig)); +} + +void YGNodeSetIsReferenceBaseline(YGNodeRef node, bool isReferenceBaseline) { + if (node->isReferenceBaseline() != isReferenceBaseline) { + node->setIsReferenceBaseline(isReferenceBaseline); + node->markDirtyAndPropogate(); + } +} + +bool YGNodeIsReferenceBaseline(YGNodeRef node) { + return node->isReferenceBaseline(); +} + +void YGNodeInsertChild( + const YGNodeRef owner, + const YGNodeRef child, + const uint32_t index) { + YGAssertWithNode( + owner, + child->getOwner() == nullptr, + "Child already has a owner, it must be removed first."); + + YGAssertWithNode( + owner, + !owner->hasMeasureFunc(), + "Cannot add child: Nodes with measure functions cannot have children."); + + owner->insertChild(child, index); + child->setOwner(owner); + owner->markDirtyAndPropogate(); +} + +void YGNodeRemoveChild(const YGNodeRef owner, const YGNodeRef excludedChild) { + if (YGNodeGetChildCount(owner) == 0) { + // This is an empty set. Nothing to remove. + return; + } + + // Children may be shared between parents, which is indicated by not having an + // owner. We only want to reset the child completely if it is owned + // exclusively by one node. + auto childOwner = excludedChild->getOwner(); + if (owner->removeChild(excludedChild)) { + if (owner == childOwner) { + excludedChild->setLayout({}); // layout is no longer valid + excludedChild->setOwner(nullptr); + } + owner->markDirtyAndPropogate(); + } +} + +void YGNodeRemoveAllChildren(const YGNodeRef owner) { + const uint32_t childCount = YGNodeGetChildCount(owner); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(owner, 0); + if (firstChild->getOwner() == owner) { + // If the first child has this node as its owner, we assume that this child + // set is unique. + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = YGNodeGetChild(owner, i); + oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid + oldChild->setOwner(nullptr); + } + owner->clearChildren(); + owner->markDirtyAndPropogate(); + return; + } + // Otherwise, we are not the owner of the child set. We don't have to do + // anything to clear it. + owner->setChildren(YGVector()); + owner->markDirtyAndPropogate(); +} + +static void YGNodeSetChildrenInternal( + YGNodeRef const owner, + const std::vector& children) { + if (!owner) { + return; + } + if (children.size() == 0) { + if (YGNodeGetChildCount(owner) > 0) { + for (YGNodeRef const child : owner->getChildren()) { + child->setLayout(YGLayout()); + child->setOwner(nullptr); + } + owner->setChildren(YGVector()); + owner->markDirtyAndPropogate(); + } + } else { + if (YGNodeGetChildCount(owner) > 0) { + for (YGNodeRef const oldChild : owner->getChildren()) { + // Our new children may have nodes in common with the old children. We + // don't reset these common nodes. + if (std::find(children.begin(), children.end(), oldChild) == + children.end()) { + oldChild->setLayout(YGLayout()); + oldChild->setOwner(nullptr); + } + } + } + owner->setChildren(children); + for (YGNodeRef child : children) { + child->setOwner(owner); + } + owner->markDirtyAndPropogate(); + } +} + +void YGNodeSetChildren( + YGNodeRef const owner, + const YGNodeRef c[], + const uint32_t count) { + const YGVector children = {c, c + count}; + YGNodeSetChildrenInternal(owner, children); +} + +void YGNodeSetChildren( + YGNodeRef const owner, + const std::vector& children) { + YGNodeSetChildrenInternal(owner, children); +} + +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 YGNodeGetOwner(const YGNodeRef node) { + return node->getOwner(); +} + +YGNodeRef YGNodeGetParent(const YGNodeRef node) { + return node->getOwner(); +} + +void YGNodeMarkDirty(const YGNodeRef node) { + YGAssertWithNode( + node, + node->hasMeasureFunc(), + "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 node->getStyle().flexGrow.isUndefined() + ? kDefaultFlexGrow + : node->getStyle().flexGrow.unwrap(); +} + +float YGNodeStyleGetFlexShrink(const YGNodeRef node) { + return node->getStyle().flexShrink.isUndefined() + ? (node->getConfig()->useWebDefaults ? kWebDefaultFlexShrink + : kDefaultFlexShrink) + : node->getStyle().flexShrink.unwrap(); +} + +namespace { + +struct Value { + template + static detail::CompactValue create(float value) { + return detail::CompactValue::ofMaybe(value); + } +}; + +template <> +inline detail::CompactValue Value::create(float) { + return detail::CompactValue::ofUndefined(); +} + +template <> +inline detail::CompactValue Value::create(float) { + return detail::CompactValue::ofAuto(); +} + +template +struct DimensionProp { + template + static YGValue get(YGNodeRef node) { + YGValue value = (node->getStyle().*P)[idx]; + if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { + value.value = YGUndefined; + } + return value; + } + + template + static void set(YGNodeRef node, float newValue) { + auto value = Value::create(newValue); + if ((node->getStyle().*P)[idx] != value) { + (node->getStyle().*P)[idx] = value; + node->markDirtyAndPropogate(); + } + } +}; + +} // namespace + +#define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \ + type, name, paramName, instanceName) \ + void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \ + auto value = detail::CompactValue::ofMaybe(paramName); \ + if (node->getStyle().instanceName != value) { \ + node->getStyle().instanceName = value; \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const type paramName) { \ + auto value = detail::CompactValue::ofMaybe(paramName); \ + if (node->getStyle().instanceName != value) { \ + node->getStyle().instanceName = value; \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \ + if (node->getStyle().instanceName != detail::CompactValue::ofAuto()) { \ + node->getStyle().instanceName = detail::CompactValue::ofAuto(); \ + node->markDirtyAndPropogate(); \ + } \ + } + +#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) { \ + YGValue value = node->getStyle().instanceName; \ + if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \ + value.value = YGUndefined; \ + } \ + return value; \ + } + +#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] != \ + detail::CompactValue::ofAuto()) { \ + node->getStyle().instanceName[edge] = detail::CompactValue::ofAuto(); \ + 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) { \ + auto value = detail::CompactValue::ofMaybe(paramName); \ + if (node->getStyle().instanceName[edge] != value) { \ + node->getStyle().instanceName[edge] = value; \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + void YGNodeStyleSet##name##Percent( \ + const YGNodeRef node, const YGEdge edge, const float paramName) { \ + auto value = detail::CompactValue::ofMaybe(paramName); \ + if (node->getStyle().instanceName[edge] != value) { \ + node->getStyle().instanceName[edge] = value; \ + node->markDirtyAndPropogate(); \ + } \ + } \ + \ + type YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \ + YGValue value = node->getStyle().instanceName[edge]; \ + if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \ + value.value = YGUndefined; \ + } \ + return 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]; \ + } + +#define YG_NODE_STYLE_SET(node, property, value) \ + if (node->getStyle().property != value) { \ + node->getStyle().property = value; \ + node->markDirtyAndPropogate(); \ + } + +void YGNodeStyleSetDirection(const YGNodeRef node, const YGDirection value) { + YG_NODE_STYLE_SET(node, direction, value); +} +YGDirection YGNodeStyleGetDirection(const YGNodeRef node) { + return node->getStyle().direction; +} + +void YGNodeStyleSetFlexDirection( + const YGNodeRef node, + const YGFlexDirection flexDirection) { + YG_NODE_STYLE_SET(node, flexDirection, flexDirection); +} +YGFlexDirection YGNodeStyleGetFlexDirection(const YGNodeRef node) { + return node->getStyle().flexDirection; +} + +void YGNodeStyleSetJustifyContent( + const YGNodeRef node, + const YGJustify justifyContent) { + YG_NODE_STYLE_SET(node, justifyContent, justifyContent); +} +YGJustify YGNodeStyleGetJustifyContent(const YGNodeRef node) { + return node->getStyle().justifyContent; +} + +void YGNodeStyleSetAlignContent( + const YGNodeRef node, + const YGAlign alignContent) { + YG_NODE_STYLE_SET(node, alignContent, alignContent); +} +YGAlign YGNodeStyleGetAlignContent(const YGNodeRef node) { + return node->getStyle().alignContent; +} + +void YGNodeStyleSetAlignItems(const YGNodeRef node, const YGAlign alignItems) { + YG_NODE_STYLE_SET(node, alignItems, alignItems); +} +YGAlign YGNodeStyleGetAlignItems(const YGNodeRef node) { + return node->getStyle().alignItems; +} + +void YGNodeStyleSetAlignSelf(const YGNodeRef node, const YGAlign alignSelf) { + YG_NODE_STYLE_SET(node, alignSelf, alignSelf); +} +YGAlign YGNodeStyleGetAlignSelf(const YGNodeRef node) { + return node->getStyle().alignSelf; +} + +void YGNodeStyleSetPositionType( + const YGNodeRef node, + const YGPositionType positionType) { + YG_NODE_STYLE_SET(node, positionType, positionType); +} +YGPositionType YGNodeStyleGetPositionType(const YGNodeRef node) { + return node->getStyle().positionType; +} + +void YGNodeStyleSetFlexWrap(const YGNodeRef node, const YGWrap flexWrap) { + YG_NODE_STYLE_SET(node, flexWrap, flexWrap); +} +YGWrap YGNodeStyleGetFlexWrap(const YGNodeRef node) { + return node->getStyle().flexWrap; +} + +void YGNodeStyleSetOverflow(const YGNodeRef node, const YGOverflow overflow) { + YG_NODE_STYLE_SET(node, overflow, overflow); +} +YGOverflow YGNodeStyleGetOverflow(const YGNodeRef node) { + return node->getStyle().overflow; +} + +void YGNodeStyleSetDisplay(const YGNodeRef node, const YGDisplay display) { + YG_NODE_STYLE_SET(node, display, display); +} +YGDisplay YGNodeStyleGetDisplay(const YGNodeRef node) { + return node->getStyle().display; +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) { + if (node->getStyle().flex != flex) { + node->getStyle().flex = + YGFloatIsUndefined(flex) ? YGFloatOptional() : YGFloatOptional(flex); + node->markDirtyAndPropogate(); + } +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +float YGNodeStyleGetFlex(const YGNodeRef node) { + return node->getStyle().flex.isUndefined() ? YGUndefined + : node->getStyle().flex.unwrap(); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) { + if (node->getStyle().flexGrow != flexGrow) { + node->getStyle().flexGrow = YGFloatIsUndefined(flexGrow) + ? YGFloatOptional() + : YGFloatOptional(flexGrow); + node->markDirtyAndPropogate(); + } +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) { + if (node->getStyle().flexShrink != flexShrink) { + node->getStyle().flexShrink = YGFloatIsUndefined(flexShrink) + ? YGFloatOptional() + : YGFloatOptional(flexShrink); + node->markDirtyAndPropogate(); + } +} + +YGValue YGNodeStyleGetFlexBasis(const YGNodeRef node) { + YGValue flexBasis = node->getStyle().flexBasis; + if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) { + // TODO(T26792433): Get rid off the use of YGUndefined at client side + flexBasis.value = YGUndefined; + } + return flexBasis; +} + +void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) { + auto value = detail::CompactValue::ofMaybe(flexBasis); + if (node->getStyle().flexBasis != value) { + node->getStyle().flexBasis = value; + node->markDirtyAndPropogate(); + } +} + +void YGNodeStyleSetFlexBasisPercent( + const YGNodeRef node, + const float flexBasisPercent) { + auto value = detail::CompactValue::ofMaybe(flexBasisPercent); + if (node->getStyle().flexBasis != value) { + node->getStyle().flexBasis = value; + node->markDirtyAndPropogate(); + } +} + +void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) { + if (node->getStyle().flexBasis != detail::CompactValue::ofAuto()) { + node->getStyle().flexBasis = detail::CompactValue::ofAuto(); + node->markDirtyAndPropogate(); + } +} + +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); + +// TODO(T26792433): Change the API to accept YGFloatOptional. +void YGNodeStyleSetBorder( + const YGNodeRef node, + const YGEdge edge, + const float border) { + auto value = detail::CompactValue::ofMaybe(border); + if (node->getStyle().border[edge] != value) { + node->getStyle().border[edge] = value; + node->markDirtyAndPropogate(); + } +} + +float YGNodeStyleGetBorder(const YGNodeRef node, const YGEdge edge) { + if (node->getStyle().border[edge].isUndefined() || + node->getStyle().border[edge].isAuto()) { + // TODO(T26792433): Rather than returning YGUndefined, change the api to + // return YGFloatOptional. + return YGUndefined; + } + + auto border = (YGValue) node->getStyle().border[edge]; + return border.value; +} + +// Yoga specific properties, not compatible with flexbox specification + +// TODO(T26792433): Change the API to accept YGFloatOptional. +float YGNodeStyleGetAspectRatio(const YGNodeRef node) { + const YGFloatOptional op = node->getStyle().aspectRatio; + return op.isUndefined() ? YGUndefined : op.unwrap(); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) { + if (node->getStyle().aspectRatio != aspectRatio) { + node->getStyle().aspectRatio = YGFloatOptional(aspectRatio); + node->markDirtyAndPropogate(); + } +} + +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL( + YGValue, + Width, + width, + dimensions[YGDimensionWidth]); +YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL( + YGValue, + Height, + height, + dimensions[YGDimensionHeight]); + +void YGNodeStyleSetMinWidth(const YGNodeRef node, const float minWidth) { + DimensionProp<&YGStyle::minDimensions>::set( + node, minWidth); +} +void YGNodeStyleSetMinWidthPercent(const YGNodeRef node, const float minWidth) { + DimensionProp<&YGStyle::minDimensions>::set( + node, minWidth); +} +YGValue YGNodeStyleGetMinWidth(const YGNodeRef node) { + return DimensionProp<&YGStyle::minDimensions>::get(node); +}; + +void YGNodeStyleSetMinHeight(const YGNodeRef node, const float minHeight) { + DimensionProp<&YGStyle::minDimensions>::set( + node, minHeight); +} +void YGNodeStyleSetMinHeightPercent( + const YGNodeRef node, + const float minHeight) { + DimensionProp<&YGStyle::minDimensions>::set( + node, minHeight); +} +YGValue YGNodeStyleGetMinHeight(const YGNodeRef node) { + return DimensionProp<&YGStyle::minDimensions>::get(node); +}; + +void YGNodeStyleSetMaxWidth(const YGNodeRef node, const float maxWidth) { + DimensionProp<&YGStyle::maxDimensions>::set( + node, maxWidth); +} +void YGNodeStyleSetMaxWidthPercent(const YGNodeRef node, const float maxWidth) { + DimensionProp<&YGStyle::maxDimensions>::set( + node, maxWidth); +} +YGValue YGNodeStyleGetMaxWidth(const YGNodeRef node) { + return DimensionProp<&YGStyle::maxDimensions>::get(node); +}; + +void YGNodeStyleSetMaxHeight(const YGNodeRef node, const float maxHeight) { + DimensionProp<&YGStyle::maxDimensions>::set( + node, maxHeight); +} +void YGNodeStyleSetMaxHeightPercent( + const YGNodeRef node, + const float maxHeight) { + DimensionProp<&YGStyle::maxDimensions>::set( + node, maxHeight); +} +YGValue YGNodeStyleGetMaxHeight(const YGNodeRef node) { + return DimensionProp<&YGStyle::maxDimensions>::get(node); +}; + +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); + +bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(const YGNodeRef node) { + return node->getLayout().doesLegacyStretchFlagAffectsLayout; +} + +uint32_t gCurrentGenerationCount = 0; + +bool YGLayoutNodeInternal( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection ownerDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const char* reason, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext); + +#ifdef DEBUG +static void YGNodePrintInternal( + const YGNodeRef node, + const YGPrintOptions options) { + std::string str; + facebook::yoga::YGNodeToString(str, node, options, 0); + Log::log(node, YGLogLevelDebug, nullptr, str.c_str()); +} + +void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) { + YGNodePrintInternal(node, options); +} +#endif + +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)) + .unwrap(); +} + +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, void* layoutContext) { + if (node->hasBaselineFunc()) { + const float baseline = marker::MarkerSection::wrap( + node, + &YGNode::baseline, + node->getLayout().measuredDimensions[YGDimensionWidth], + node->getLayout().measuredDimensions[YGDimensionHeight], + layoutContext); + 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 || + child->isReferenceBaseline()) { + baselineChild = child; + break; + } + + if (baselineChild == nullptr) { + baselineChild = child; + } + } + + if (baselineChild == nullptr) { + return node->getLayout().measuredDimensions[YGDimensionHeight]; + } + + const float baseline = YGBaseline(baselineChild, layoutContext); + 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)) + .unwrap(); +} + +static inline bool YGNodeIsStyleDimDefined( + const YGNodeRef node, + const YGFlexDirection axis, + const float ownerSize) { + bool isUndefined = + YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value); + return !( + node->getResolvedDimension(dim[axis]).unit == YGUnitAuto || + node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint && + !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent && + !isUndefined && + (node->getResolvedDimension(dim[axis]).value < 0.0f || + YGFloatIsUndefined(ownerSize)))); +} + +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 YGFloatOptional YGNodeBoundAxisWithinMinAndMax( + const YGNodeRef node, + const YGFlexDirection axis, + const YGFloatOptional value, + const float axisSize) { + YGFloatOptional min; + YGFloatOptional max; + + 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); + } + + if (max >= YGFloatOptional{0} && value > max) { + return max; + } + + if (min >= YGFloatOptional{0} && value < min) { + return min; + } + + return value; +} + +// 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 YGFloatMax( + YGNodeBoundAxisWithinMinAndMax( + node, axis, YGFloatOptional{value}, axisSize) + .unwrap(), + 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 ownerAxisSize, + const float ownerWidth, + YGMeasureMode* mode, + float* size) { + const YGFloatOptional maxSize = + YGResolveValue(node->getStyle().maxDimensions[dim[axis]], ownerAxisSize) + + YGFloatOptional(node->getMarginForAxis(axis, ownerWidth)); + switch (*mode) { + case YGMeasureModeExactly: + case YGMeasureModeAtMost: + *size = (maxSize.isUndefined() || *size < maxSize.unwrap()) + ? *size + : maxSize.unwrap(); + break; + case YGMeasureModeUndefined: + if (!maxSize.isUndefined()) { + *mode = YGMeasureModeAtMost; + *size = maxSize.unwrap(); + } + break; + } +} + +static void YGNodeComputeFlexBasisForChild( + const YGNodeRef node, + const YGNodeRef child, + const float width, + const YGMeasureMode widthMode, + const float height, + const float ownerWidth, + const float ownerHeight, + const YGMeasureMode heightMode, + const YGDirection direction, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->getStyle().flexDirection, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const float mainAxisSize = isMainAxisRow ? width : height; + const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; + + float childWidth; + float childHeight; + YGMeasureMode childWidthMeasureMode; + YGMeasureMode childHeightMeasureMode; + + const YGFloatOptional resolvedFlexBasis = + YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize); + const bool isRowStyleDimDefined = + YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth); + const bool isColumnStyleDimDefined = + YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, ownerHeight); + + if (!resolvedFlexBasis.isUndefined() && !YGFloatIsUndefined(mainAxisSize)) { + if (child->getLayout().computedFlexBasis.isUndefined() || + (YGConfigIsExperimentalFeatureEnabled( + child->getConfig(), YGExperimentalFeatureWebFlexBasis) && + child->getLayout().computedFlexBasisGeneration != + gCurrentGenerationCount)) { + const YGFloatOptional paddingAndBorder = YGFloatOptional( + YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth)); + child->setLayoutComputedFlexBasis( + YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder)); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + const YGFloatOptional paddingAndBorder = YGFloatOptional( + YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth)); + + child->setLayoutComputedFlexBasis(YGFloatOptionalMax( + YGResolveValue( + child->getResolvedDimension(YGDimensionWidth), ownerWidth), + paddingAndBorder)); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + const YGFloatOptional paddingAndBorder = + YGFloatOptional(YGNodePaddingAndBorderForAxis( + child, YGFlexDirectionColumn, ownerWidth)); + child->setLayoutComputedFlexBasis(YGFloatOptionalMax( + YGResolveValue( + child->getResolvedDimension(YGDimensionHeight), ownerHeight), + paddingAndBorder)); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex + // basis). + childWidth = YGUndefined; + childHeight = YGUndefined; + childWidthMeasureMode = YGMeasureModeUndefined; + childHeightMeasureMode = YGMeasureModeUndefined; + + auto marginRow = + child->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + auto marginColumn = + child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + if (isRowStyleDimDefined) { + childWidth = + YGResolveValue( + child->getResolvedDimension(YGDimensionWidth), ownerWidth) + .unwrap() + + marginRow; + childWidthMeasureMode = YGMeasureModeExactly; + } + if (isColumnStyleDimDefined) { + childHeight = + YGResolveValue( + child->getResolvedDimension(YGDimensionHeight), ownerHeight) + .unwrap() + + 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 (!child->getStyle().aspectRatio.isUndefined()) { + if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) { + childHeight = marginColumn + + (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap(); + childHeightMeasureMode = YGMeasureModeExactly; + } else if ( + isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) { + childWidth = marginRow + + (childHeight - marginColumn) * + child->getStyle().aspectRatio.unwrap(); + 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 (!child->getStyle().aspectRatio.isUndefined()) { + childHeight = + (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap(); + 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 (!child->getStyle().aspectRatio.isUndefined()) { + childWidth = (childHeight - marginColumn) * + child->getStyle().aspectRatio.unwrap(); + childWidthMeasureMode = YGMeasureModeExactly; + } + } + + YGConstrainMaxSizeForMode( + child, + YGFlexDirectionRow, + ownerWidth, + ownerWidth, + &childWidthMeasureMode, + &childWidth); + YGConstrainMaxSizeForMode( + child, + YGFlexDirectionColumn, + ownerHeight, + ownerWidth, + &childHeightMeasureMode, + &childHeight); + + // Measure the child + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + ownerWidth, + ownerHeight, + false, + "measure", + config, + layoutMarkerData, + layoutContext); + + child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax( + child->getLayout().measuredDimensions[dim[mainAxis]], + YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth)))); + } + 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, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + 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; + + auto marginRow = child->getMarginForAxis(YGFlexDirectionRow, width).unwrap(); + auto marginColumn = + child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap(); + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) { + childWidth = + YGResolveValue(child->getResolvedDimension(YGDimensionWidth), width) + .unwrap() + + 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)) + .unwrap(); + childWidth = + YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width); + } + } + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { + childHeight = + YGResolveValue(child->getResolvedDimension(YGDimensionHeight), height) + .unwrap() + + 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)) + .unwrap(); + 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 (!child->getStyle().aspectRatio.isUndefined()) { + if (YGFloatIsUndefined(childWidth)) { + childWidth = marginRow + + (childHeight - marginColumn) * + child->getStyle().aspectRatio.unwrap(); + } else if (YGFloatIsUndefined(childHeight)) { + childHeight = marginColumn + + (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap(); + } + } + } + + // 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 owner 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 owner. This is the same behavior as many browsers + // implement. + if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && + widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) && + width > 0) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + childWidth, + childHeight, + false, + "abs-measure", + config, + layoutMarkerData, + layoutContext); + childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] + + child->getMarginForAxis(YGFlexDirectionRow, width).unwrap(); + childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap(); + } + + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + childWidth, + childHeight, + true, + "abs-layout", + config, + layoutMarkerData, + layoutContext); + + 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).unwrap() - + child->getTrailingPosition(mainAxis, isMainAxisRow ? width : height) + .unwrap(), + 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).unwrap() - + child + ->getTrailingPosition(crossAxis, isMainAxisRow ? height : width) + .unwrap(), + 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 ownerWidth, + const float ownerHeight, + void* const layoutContext) { + YGAssertWithNode( + node, + node->hasMeasureFunc(), + "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).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, availableWidth).unwrap(); + + // We want to make sure we don't call measure with negative size + const float innerWidth = YGFloatIsUndefined(availableWidth) + ? availableWidth + : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + const float innerHeight = YGFloatIsUndefined(availableHeight) + ? availableHeight + : YGFloatMax( + 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, + ownerWidth, + ownerWidth), + YGDimensionWidth); + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + } else { + // Measure the text under the current constraints. + const YGSize measuredSize = marker::MarkerSection::wrap( + node, + &YGNode::measure, + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode, + layoutContext); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + 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 ownerWidth, + const float ownerHeight) { + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); +} + +static bool YGNodeFixedSizeSetMeasuredDimensions( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight) { + if ((!YGFloatIsUndefined(availableWidth) && + widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || + (!YGFloatIsUndefined(availableHeight) && + heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || + (widthMeasureMode == YGMeasureModeExactly && + heightMeasureMode == YGMeasureModeExactly)) { + auto marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + auto marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + YGFloatIsUndefined(availableWidth) || + (widthMeasureMode == YGMeasureModeAtMost && + availableWidth < 0.0f) + ? 0.0f + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + YGFloatIsUndefined(availableHeight) || + (heightMeasureMode == YGMeasureModeAtMost && + availableHeight < 0.0f) + ? 0.0f + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + return true; + } + + return false; +} + +static void YGZeroOutLayoutRecursivly( + const YGNodeRef node, + void* layoutContext) { + node->getLayout() = {}; + node->setLayoutDimension(0, 0); + node->setLayoutDimension(0, 1); + node->setHasNewLayout(true); + + node->iterChildrenAfterCloningIfNeeded( + YGZeroOutLayoutRecursivly, layoutContext); +} + +static float YGNodeCalculateAvailableInnerDim( + const YGNodeRef node, + YGFlexDirection axis, + float availableDim, + float ownerDim) { + YGFlexDirection direction = + YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn; + YGDimension dimension = + YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight; + + const float margin = node->getMarginForAxis(direction, ownerDim).unwrap(); + const float paddingAndBorder = + YGNodePaddingAndBorderForAxis(node, direction, ownerDim); + + 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 YGFloatOptional minDimensionOptional = + YGResolveValue(node->getStyle().minDimensions[dimension], ownerDim); + const float minInnerDim = minDimensionOptional.isUndefined() + ? 0.0f + : minDimensionOptional.unwrap() - paddingAndBorder; + + const YGFloatOptional maxDimensionOptional = + YGResolveValue(node->getStyle().maxDimensions[dimension], ownerDim); + + const float maxInnerDim = maxDimensionOptional.isUndefined() + ? FLT_MAX + : maxDimensionOptional.unwrap() - paddingAndBorder; + availableInnerDim = + YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim); + } + + return availableInnerDim; +} + +static float YGNodeComputeFlexBasisForChildren( + const YGNodeRef node, + const float availableInnerWidth, + const float availableInnerHeight, + YGMeasureMode widthMeasureMode, + YGMeasureMode heightMeasureMode, + YGDirection direction, + YGFlexDirection mainAxis, + const YGConfigRef config, + bool performLayout, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + float totalOuterFlexBasis = 0.0f; + 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 (child->isNodeFlexible()) { + if (singleFlexChild != nullptr || + YGFloatsEqual(child->resolveFlexGrow(), 0.0f) || + YGFloatsEqual(child->resolveFlexShrink(), 0.0f)) { + // There is already a flexible child, or this flexible child doesn't + // have flexGrow and flexShrink, abort + singleFlexChild = nullptr; + break; + } else { + singleFlexChild = child; + } + } + } + } + + for (auto child : children) { + child->resolveDimension(); + if (child->getStyle().display == YGDisplayNone) { + YGZeroOutLayoutRecursivly(child, layoutContext); + child->setHasNewLayout(true); + child->setDirty(false); + continue; + } + if (performLayout) { + // Set the initial position (relative to the owner). + 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(YGFloatOptional(0)); + } else { + YGNodeComputeFlexBasisForChild( + node, + child, + availableInnerWidth, + widthMeasureMode, + availableInnerHeight, + availableInnerWidth, + availableInnerHeight, + heightMeasureMode, + direction, + config, + layoutMarkerData, + layoutContext); + } + + totalOuterFlexBasis += + (child->getLayout().computedFlexBasis + + child->getMarginForAxis(mainAxis, availableInnerWidth)) + .unwrap(); + } + + return totalOuterFlexBasis; +} + +// 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 ownerDirection, + const float mainAxisownerSize, + 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(ownerDirection)); + 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->getChildren().size(); 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).unwrap(); + const float flexBasisWithMinAndMaxConstraints = + YGNodeBoundAxisWithinMinAndMax( + child, + mainAxis, + child->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + + // 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.unwrap(); + } + + 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 mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + 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, + mainAxisownerSize) + .unwrap(); + float updatedMainSize = childFlexBasis; + + if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentRelativeChild->resolveFlexShrink() * childFlexBasis; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize; + + if (!YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexShrinkScaledFactors) && + collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = childFlexBasis + + (collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors) * + flexShrinkScaledFactor; + } + + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * + flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace += updatedMainSize - childFlexBasis; + + const float marginMain = + currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth) + .unwrap(); + const float marginCross = + currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap(); + + float childCrossSize; + float childMainSize = updatedMainSize + marginMain; + YGMeasureMode childCrossMeasureMode; + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + + if (!currentRelativeChild->getStyle().aspectRatio.isUndefined()) { + childCrossSize = isMainAxisRow ? (childMainSize - marginMain) / + currentRelativeChild->getStyle().aspectRatio.unwrap() + : (childMainSize - marginMain) * + currentRelativeChild->getStyle().aspectRatio.unwrap(); + 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) + .unwrap() + + 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, + layoutMarkerData, + layoutContext); + 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 mainAxisownerSize, + 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, + mainAxisownerSize) + .unwrap(); + + if (collectedFlexItemsValues.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentRelativeChild->resolveFlexShrink() * childFlexBasis; + + // Is this child able to shrink? + if (!YGFloatIsUndefined(flexShrinkScaledFactor) && + flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors * + flexShrinkScaledFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + 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 ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + 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 +// the first pass. 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/CSS-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 mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace; + // First pass: detect the flex items whose min/max constraints trigger + YGDistributeFreeSpaceFirstPass( + collectedFlexItemsValues, + mainAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerWidth); + + // Second pass: resolve the sizes of the flexible items + const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass( + collectedFlexItemsValues, + node, + mainAxis, + crossAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config, + layoutMarkerData, + layoutContext); + + 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 mainAxisownerSize, + const float ownerWidth, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const bool performLayout, + void* const layoutContext) { + const YGStyle& style = node->getStyle(); + const float leadingPaddingAndBorderMain = + node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap(); + const float trailingPaddingAndBorderMain = + node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap(); + // If we are using "at most" rules in the main axis, make sure that + // remainingFreeSpace is 0 when min main dimension is not given + if (measureModeMainDim == YGMeasureModeAtMost && + collectedFlexItemsValues.remainingFreeSpace > 0) { + if (!style.minDimensions[dim[mainAxis]].isUndefined() && + !YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize) + .isUndefined()) { + // This condition makes sure that if the size of main dimension(after + // considering child nodes main dim, leading and trailing padding etc) + // falls below min dimension, then the remainingFreeSpace is reassigned + // considering the min dimension + + // `minAvailableMainDim` denotes minimum available space in which child + // can be laid out, it will exclude space consumed by padding and border. + const float minAvailableMainDim = + YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize) + .unwrap() - + leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; + const float occupiedSpaceByChildNodes = + availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace; + collectedFlexItemsValues.remainingFreeSpace = + YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes); + } 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 = + YGFloatMax(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; + } + } + + collectedFlexItemsValues.mainDim = + leadingPaddingAndBorderMain + leadingMainDim; + collectedFlexItemsValues.crossDim = 0; + + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + bool isNodeBaselineLayout = YGIsBaselineLayout(node); + 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) + .unwrap() + + node->getLeadingBorder(mainAxis) + + child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(), + 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).unwrap() + + childLayout.computedFlexBasis.unwrap(); + 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); + + if (isNodeBaselineLayout) { + // If the child is baseline aligned then the cross dimension is + // calculated by adding maxAscent and maxDescent from the baseline. + const float ascent = YGBaseline(child, layoutContext) + + child + ->getLeadingMargin( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap(); + const float descent = + child->getLayout().measuredDimensions[YGDimensionHeight] + + child + ->getMarginForAxis( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap() - + ascent; + + maxAscentForCurrentLine = + YGFloatMax(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + YGFloatMax(maxDescentForCurrentLine, descent); + } else { + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension in the case + // when the items are not baseline aligned + collectedFlexItemsValues.crossDim = YGFloatMax( + collectedFlexItemsValues.crossDim, + YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); + } + } + } else if (performLayout) { + child->setLayoutPosition( + childLayout.position[pos[mainAxis]] + + node->getLeadingBorder(mainAxis) + leadingMainDim, + pos[mainAxis]); + } + } + } + collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain; + + if (isNodeBaselineLayout) { + collectedFlexItemsValues.crossDim = + maxAscentForCurrentLine + maxDescentForCurrentLine; + } +} + +// +// This is the main routine that implements a subset of the flexbox layout +// algorithm described in the W3C CSS documentation: +// https://www.w3.org/TR/CSS3-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 +// - ownerDirection: the inline (text) direction within the owner +// (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/CSS3-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 ownerDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + 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"); + + (performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1; + + // Set the resolved resolution in the node's layout. + const YGDirection direction = node->resolveDirection(ownerDirection); + node->setLayoutDirection(direction); + + const YGFlexDirection flexRowDirection = + YGResolveFlexDirection(YGFlexDirectionRow, direction); + const YGFlexDirection flexColumnDirection = + YGResolveFlexDirection(YGFlexDirectionColumn, direction); + + node->setLayoutMargin( + node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap(), + YGEdgeStart); + node->setLayoutMargin( + node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap(), + YGEdgeEnd); + node->setLayoutMargin( + node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap(), + YGEdgeTop); + node->setLayoutMargin( + node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap(), + 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, ownerWidth).unwrap(), + YGEdgeStart); + node->setLayoutPadding( + node->getTrailingPadding(flexRowDirection, ownerWidth).unwrap(), + YGEdgeEnd); + node->setLayoutPadding( + node->getLeadingPadding(flexColumnDirection, ownerWidth).unwrap(), + YGEdgeTop); + node->setLayoutPadding( + node->getTrailingPadding(flexColumnDirection, ownerWidth).unwrap(), + YGEdgeBottom); + + if (node->hasMeasureFunc()) { + YGNodeWithMeasureFuncSetMeasuredDimensions( + node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + layoutContext); + return; + } + + const uint32_t childCount = YGNodeGetChildCount(node); + if (childCount == 0) { + YGNodeEmptyContainerSetMeasuredDimensions( + node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight); + 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, + ownerWidth, + ownerHeight)) { + return; + } + + // At this point we know we're going to perform work. Ensure that each child + // has a mutable copy. + node->cloneChildrenIfNeeded(layoutContext); + // 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 mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; + const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth; + + const float leadingPaddingAndBorderCross = + node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap(); + const float paddingAndBorderAxisMain = + YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth); + const float paddingAndBorderAxisCross = + YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth); + + 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, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + const float minInnerWidth = + YGResolveValue( + node->getStyle().minDimensions[YGDimensionWidth], ownerWidth) + .unwrap() - + paddingAndBorderAxisRow; + const float maxInnerWidth = + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth) + .unwrap() - + paddingAndBorderAxisRow; + const float minInnerHeight = + YGResolveValue( + node->getStyle().minDimensions[YGDimensionHeight], ownerHeight) + .unwrap() - + paddingAndBorderAxisColumn; + const float maxInnerHeight = + YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight) + .unwrap() - + 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, ownerWidth); + float availableInnerHeight = YGNodeCalculateAvailableInnerDim( + node, YGFlexDirectionColumn, availableHeight, ownerHeight); + + float availableInnerMainDim = + isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = + isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + + float totalOuterFlexBasis = YGNodeComputeFlexBasisForChildren( + node, + availableInnerWidth, + availableInnerHeight, + widthMeasureMode, + heightMeasureMode, + direction, + mainAxis, + config, + performLayout, + layoutMarkerData, + layoutContext); + + 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, + ownerDirection, + mainAxisownerSize, + 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 && + ((YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexGrowFactors) && + collectedFlexItemsValues.totalFlexGrowFactors == 0) || + (YGFloatIsUndefined(node->resolveFlexGrow()) && + 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, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config, + layoutMarkerData, + layoutContext); + } + + 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, + mainAxisownerSize, + ownerWidth, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + performLayout, + layoutContext); + + 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, + crossAxisownerSize, + ownerWidth) - + 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, + crossAxisownerSize, + ownerWidth) - + 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) + .unwrap() + + node->getLeadingBorder(crossAxis) + + child->getLeadingMargin(crossAxis, availableInnerWidth) + .unwrap(), + 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) + .unwrap(), + pos[crossAxis]); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (owner) 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 = + !child->getStyle().aspectRatio.isUndefined() + ? child->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap() + + (isMainAxisRow ? childMainSize / + child->getStyle().aspectRatio.unwrap() + : childMainSize * + child->getStyle().aspectRatio.unwrap()) + : collectedFlexItemsValues.crossDim; + + childMainSize += + child->getMarginForAxis(mainAxis, availableInnerWidth) + .unwrap(); + + 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; + + auto alignContent = node->getStyle().alignContent; + auto crossAxisDoesNotGrow = + alignContent != YGAlignStretch && isNodeFlexWrap; + const YGMeasureMode childWidthMeasureMode = + YGFloatIsUndefined(childWidth) || + (!isMainAxisRow && crossAxisDoesNotGrow) + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + const YGMeasureMode childHeightMeasureMode = + YGFloatIsUndefined(childHeight) || + (isMainAxisRow && crossAxisDoesNotGrow) + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + availableInnerWidth, + availableInnerHeight, + true, + "stretch", + config, + layoutMarkerData, + layoutContext); + } + } else { + const float remainingCrossDim = containerCrossAxis - + YGNodeDimWithMargin(child, crossAxis, availableInnerWidth); + + if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto && + child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { + leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2); + } else if ( + child->marginTrailingValue(crossAxis).unit == YGUnitAuto) { + // No-Op + } else if ( + child->marginLeadingValue(crossAxis).unit == YGUnitAuto) { + leadingCrossDim += YGFloatMax(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 = + YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + // currentLead stores the size of the cross dim + if (performLayout && (isNodeFlexWrap || YGIsBaselineLayout(node))) { + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + if (!YGFloatIsUndefined(availableInnerCrossDim)) { + const float remainingAlignContentDim = + availableInnerCrossDim - totalLineCrossDim; + 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 = YGFloatMax( + lineHeight, + child->getLayout().measuredDimensions[dim[crossAxis]] + + child->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap()); + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + const float ascent = YGBaseline(child, layoutContext) + + child + ->getLeadingMargin( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap(); + const float descent = + child->getLayout().measuredDimensions[YGDimensionHeight] + + child + ->getMarginForAxis( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap() - + ascent; + maxAscentForCurrentLine = + YGFloatMax(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + YGFloatMax(maxDescentForCurrentLine, descent); + lineHeight = YGFloatMax( + 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) + .unwrap(), + pos[crossAxis]); + break; + } + case YGAlignFlexEnd: { + child->setLayoutPosition( + currentLead + lineHeight - + child->getTrailingMargin(crossAxis, availableInnerWidth) + .unwrap() - + 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) + .unwrap(), + pos[crossAxis]); + + // Remeasure child with the line height as it as been only + // measured with the owners height yet. + if (!YGNodeIsStyleDimDefined( + child, crossAxis, availableInnerCrossDim)) { + const float childWidth = isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionWidth] + + child->getMarginForAxis(mainAxis, availableInnerWidth) + .unwrap()) + : lineHeight; + + const float childHeight = !isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap()) + : 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, + layoutMarkerData, + layoutContext); + } + } + break; + } + case YGAlignBaseline: { + child->setLayoutPosition( + currentLead + maxAscentForCurrentLine - + YGBaseline(child, layoutContext) + + child + ->getLeadingPosition( + YGFlexDirectionColumn, availableInnerCrossDim) + .unwrap(), + YGEdgeTop); + + break; + } + case YGAlignAuto: + case YGAlignSpaceBetween: + case YGAlignSpaceAround: + break; + } + } + } + } + currentLead += lineHeight; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + 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, mainAxisownerSize, ownerWidth), + dim[mainAxis]); + + } else if ( + measureModeMainDim == YGMeasureModeAtMost && + node->getStyle().overflow == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + YGFloatMax( + YGFloatMin( + availableInnerMainDim + paddingAndBorderAxisMain, + YGNodeBoundAxisWithinMinAndMax( + node, + mainAxis, + YGFloatOptional{maxLineMainDim}, + mainAxisownerSize) + .unwrap()), + 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, + crossAxisownerSize, + ownerWidth), + dim[crossAxis]); + + } else if ( + measureModeCrossDim == YGMeasureModeAtMost && + node->getStyle().overflow == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + YGFloatMax( + YGFloatMin( + availableInnerCrossDim + paddingAndBorderAxisCross, + YGNodeBoundAxisWithinMinAndMax( + node, + crossAxis, + YGFloatOptional{totalLineCrossDim + + paddingAndBorderAxisCross}, + crossAxisownerSize) + .unwrap()), + 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, + layoutMarkerData, + layoutContext); + } + + // 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 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) { + constexpr auto N = enums::count(); + const char* kMeasureModeNames[N] = {"UNDEFINED", "EXACTLY", "AT_MOST"}; + const char* kLayoutModeNames[N] = { + "LAY_UNDEFINED", "LAY_EXACTLY", "LAY_AT_MOST"}; + + if (mode >= N) { + 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 && !YGFloatIsUndefined(lastSize) && + !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) && + 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; + // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue + // - fractial`. + float fractial = fmodf(scaledValue, 1.0f); + if (fractial < 0) { + // This branch is for handling negative numbers for `value`. + // + // Regarding `floor` and `ceil`. Note that for a number x, `floor(x) <= x <= + // ceil(x)` even for negative numbers. Here are a couple of examples: + // - x = 2.2: floor( 2.2) = 2, ceil( 2.2) = 3 + // - x = -2.2: floor(-2.2) = -3, ceil(-2.2) = -2 + // + // Regarding `fmodf`. For fractional negative numbers, `fmodf` returns a + // negative number. For example, `fmodf(-2.2) = -0.2`. However, we want + // `fractial` to be the number such that subtracting it from `value` will + // give us `floor(value)`. In the case of negative numbers, adding 1 to + // `fmodf(value)` gives us this. Let's continue the example from above: + // - fractial = fmodf(-2.2) = -0.2 + // - Add 1 to the fraction: fractial2 = fractial + 1 = -0.2 + 1 = 0.8 + // - Finding the `floor`: -2.2 - fractial2 = -2.2 - 0.8 = -3 + ++fractial; + } + if (YGFloatsEqual(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (YGFloatsEqual(fractial, 1.0f)) { + scaledValue = scaledValue - fractial + 1.0f; + } 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 + + (!YGFloatIsUndefined(fractial) && + (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f)) + ? 1.0f + : 0.0f); + } + return (YGFloatIsUndefined(scaledValue) || + YGFloatIsUndefined(pointScaleFactor)) + ? YGUndefined + : 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 ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) || + (!YGFloatIsUndefined(lastComputedWidth) && 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 ownerDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const char* reason, + const YGConfigRef config, + YGMarkerLayoutData& layoutMarkerData, + void* const layoutContext) { + YGLayout* layout = &node->getLayout(); + + gDepth++; + + const bool needToVisitNode = + (node->isDirty() && layout->generationCount != gCurrentGenerationCount) || + layout->lastOwnerDirection != ownerDirection; + + 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->hasMeasureFunc()) { + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + // 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; + + (performLayout ? layoutMarkerData.cachedLayouts + : layoutMarkerData.cachedMeasures) += 1; + + if (gPrintChanges && gPrintSkips) { + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "%s%d.{[skipped] ", + YGSpacer(gDepth), + gDepth); + node->print(layoutContext); + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "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) { + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "%s%d.{%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + node->print(layoutContext); + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "wm: %s, hm: %s, aw: %f ah: %f %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + availableWidth, + availableHeight, + reason); + } + + YGNodelayoutImpl( + node, + availableWidth, + availableHeight, + ownerDirection, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + performLayout, + config, + layoutMarkerData, + layoutContext); + + if (gPrintChanges) { + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "%s%d.}%s", + YGSpacer(gDepth), + gDepth, + needToVisitNode ? "*" : ""); + node->print(layoutContext); + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "wm: %s, hm: %s, d: (%f, %f) %s\n", + YGMeasureModeName(widthMeasureMode, performLayout), + YGMeasureModeName(heightMeasureMode, performLayout), + layout->measuredDimensions[YGDimensionWidth], + layout->measuredDimensions[YGDimensionHeight], + reason); + } + + layout->lastOwnerDirection = ownerDirection; + + if (cachedResults == nullptr) { + if (layout->nextCachedMeasurementsIndex + 1 > + (uint32_t) layoutMarkerData.maxMeasureCache) { + layoutMarkerData.maxMeasureCache = + layout->nextCachedMeasurementsIndex + 1; + } + if (layout->nextCachedMeasurementsIndex == usedMeasureCacheEntries) { + if (gPrintChanges) { + Log::log(node, YGLogLevelVerbose, nullptr, "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 YGNodeCalculateLayoutWithContext( + const YGNodeRef node, + const float ownerWidth, + const float ownerHeight, + const YGDirection ownerDirection, + void* layoutContext) { + marker::MarkerSection marker{node}; + + // 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, ownerWidth)) { + width = + (YGResolveValue( + node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) + + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth)) + .unwrap(); + widthMeasureMode = YGMeasureModeExactly; + } else if (!YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth) + .isUndefined()) { + width = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth) + .unwrap(); + widthMeasureMode = YGMeasureModeAtMost; + } else { + width = ownerWidth; + widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined + : YGMeasureModeExactly; + } + + float height = YGUndefined; + YGMeasureMode heightMeasureMode = YGMeasureModeUndefined; + if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) { + height = (YGResolveValue( + node->getResolvedDimension(dim[YGFlexDirectionColumn]), + ownerHeight) + + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth)) + .unwrap(); + heightMeasureMode = YGMeasureModeExactly; + } else if (!YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], + ownerHeight) + .isUndefined()) { + height = YGResolveValue( + node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight) + .unwrap(); + heightMeasureMode = YGMeasureModeAtMost; + } else { + height = ownerHeight; + heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined + : YGMeasureModeExactly; + } + if (YGLayoutNodeInternal( + node, + width, + height, + ownerDirection, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + true, + "initial", + node->getConfig(), + marker.data, + layoutContext)) { + node->setPosition( + node->getLayout().direction, ownerWidth, ownerHeight, ownerWidth); + YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f); + +#ifdef DEBUG + if (node->getConfig()->printTree) { + YGNodePrint( + node, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } +#endif + } + + // 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); + YGMarkerLayoutData layoutMarkerData; + if (YGLayoutNodeInternal( + originalNode, + width, + height, + ownerDirection, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + true, + "initial", + originalNode->getConfig(), + layoutMarkerData, + layoutContext)) { + originalNode->setPosition( + originalNode->getLayout().direction, + ownerWidth, + ownerHeight, + ownerWidth); + YGRoundToPixelGrid( + originalNode, + originalNode->getConfig()->pointScaleFactor, + 0.0f, + 0.0f); + + // Set whether the two layouts are different or not. + auto neededLegacyStretchBehaviour = + !originalNode->isLayoutTreeEqualToNode(*node); + node->setLayoutDoesLegacyFlagAffectsLayout(neededLegacyStretchBehaviour); + +#ifdef DEBUG + if (originalNode->getConfig()->printTree) { + YGNodePrint( + originalNode, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } +#endif + } + YGConfigFreeRecursive(originalNode); + YGNodeFreeRecursive(originalNode); + } +} + +void YGNodeCalculateLayout( + const YGNodeRef node, + const float ownerWidth, + const float ownerHeight, + const YGDirection ownerDirection) { + YGNodeCalculateLayoutWithContext( + node, ownerWidth, ownerHeight, ownerDirection, nullptr); +} + +void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) { + if (logger != nullptr) { + config->setLogger(logger); + } else { +#ifdef ANDROID + config->setLogger(&YGAndroidLog); +#else + config->setLogger(&YGDefaultLog); +#endif + } +} + +void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour( + const YGConfigRef config, + const bool shouldDiffLayout) { + config->shouldDiffLayoutWithoutLegacyStretchBehaviour = shouldDiffLayout; +} + +void YGAssert(const bool condition, const char* message) { + if (!condition) { + Log::log(YGNodeRef{nullptr}, YGLogLevelFatal, nullptr, "%s\n", message); + } +} + +void YGAssertWithNode( + const YGNodeRef node, + const bool condition, + const char* message) { + if (!condition) { + Log::log(node, YGLogLevelFatal, nullptr, "%s\n", message); + } +} + +void YGAssertWithConfig( + const YGConfigRef config, + const bool condition, + const char* message) { + if (!condition) { + Log::log(config, YGLogLevelFatal, nullptr, "%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 YGConfigSetCloneNodeFunc( + const YGConfigRef config, + const YGCloneNodeFunc callback) { + config->setCloneNodeCallback(callback); +} + +static void YGTraverseChildrenPreOrder( + const YGVector& children, + const std::function& f) { + for (YGNodeRef node : children) { + f(node); + YGTraverseChildrenPreOrder(node->getChildren(), f); + } +} + +void YGTraversePreOrder( + YGNodeRef const node, + std::function&& f) { + if (!node) { + return; + } + f(node); + YGTraverseChildrenPreOrder(node->getChildren(), f); +} diff --git a/Sources/yoga/Yoga.h b/Sources/yoga/Yoga.h new file mode 100755 index 0000000..753a2f9 --- /dev/null +++ b/Sources/yoga/Yoga.h @@ -0,0 +1,430 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 + +#ifndef __cplusplus +#include +#endif + +#include "YGEnums.h" +#include "YGMacros.h" +#include "YGValue.h" + +YG_EXTERN_C_BEGIN + +typedef struct YGSize { + float width; + float height; +} YGSize; + +typedef struct YGConfig* YGConfigRef; + +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 void (*YGNodeCleanupFunc)(YGNodeRef node); +typedef int (*YGLogger)( + const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args); +typedef YGNodeRef ( + *YGCloneNodeFunc)(YGNodeRef oldNode, YGNodeRef owner, 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 YGNodeFreeRecursiveWithCleanupFunc( + const YGNodeRef node, + YGNodeCleanupFunc cleanup); +WIN_EXPORT void YGNodeFreeRecursive(const YGNodeRef node); +WIN_EXPORT void YGNodeReset(const YGNodeRef node); +WIN_EXPORT int32_t YGNodeGetInstanceCount(void); + +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 YGNodeGetOwner(const YGNodeRef node); +WIN_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node); +WIN_EXPORT uint32_t YGNodeGetChildCount(const YGNodeRef node); +WIN_EXPORT void YGNodeSetChildren( + YGNodeRef const owner, + const YGNodeRef children[], + const uint32_t count); + +WIN_EXPORT void YGNodeSetIsReferenceBaseline( + YGNodeRef node, + bool isReferenceBaseline); + +WIN_EXPORT bool YGNodeIsReferenceBaseline(YGNodeRef node); + +WIN_EXPORT void YGNodeCalculateLayout( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection ownerDirection); + +// Mark a node as dirty. Only valid for nodes with a custom measure function +// set. +// +// Yoga knows when to mark all other nodes as dirty but because nodes with +// measure functions depend on information not known to Yoga they must perform +// this dirty marking manually. +WIN_EXPORT void YGNodeMarkDirty(const YGNodeRef node); + +// Marks the current node and all its descendants as dirty. +// +// Intended to be used for Uoga benchmarks. Don't use 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); + +WIN_EXPORT bool YGFloatIsUndefined(const float value); + +WIN_EXPORT 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); + +WIN_EXPORT void YGNodeCopyStyle( + const YGNodeRef dstNode, + const YGNodeRef srcNode); + +WIN_EXPORT void* YGNodeGetContext(YGNodeRef node); +WIN_EXPORT void YGNodeSetContext(YGNodeRef node, void* context); +void YGConfigSetPrintTreeFlag(YGConfigRef config, bool enabled); +bool YGNodeHasMeasureFunc(YGNodeRef node); +WIN_EXPORT void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); +bool YGNodeHasBaselineFunc(YGNodeRef node); +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc); +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node); +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc); +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc); +WIN_EXPORT bool YGNodeGetHasNewLayout(YGNodeRef node); +WIN_EXPORT void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); +YGNodeType YGNodeGetNodeType(YGNodeRef node); +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); +WIN_EXPORT bool YGNodeIsDirty(YGNodeRef node); +bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetDirection( + const YGNodeRef node, + const YGDirection direction); +WIN_EXPORT YGDirection YGNodeStyleGetDirection(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlexDirection( + const YGNodeRef node, + const YGFlexDirection flexDirection); +WIN_EXPORT YGFlexDirection YGNodeStyleGetFlexDirection(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetJustifyContent( + const YGNodeRef node, + const YGJustify justifyContent); +WIN_EXPORT YGJustify YGNodeStyleGetJustifyContent(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetAlignContent( + const YGNodeRef node, + const YGAlign alignContent); +WIN_EXPORT YGAlign YGNodeStyleGetAlignContent(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetAlignItems( + const YGNodeRef node, + const YGAlign alignItems); +WIN_EXPORT YGAlign YGNodeStyleGetAlignItems(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetAlignSelf( + const YGNodeRef node, + const YGAlign alignSelf); +WIN_EXPORT YGAlign YGNodeStyleGetAlignSelf(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetPositionType( + const YGNodeRef node, + const YGPositionType positionType); +WIN_EXPORT YGPositionType YGNodeStyleGetPositionType(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlexWrap( + const YGNodeRef node, + const YGWrap flexWrap); +WIN_EXPORT YGWrap YGNodeStyleGetFlexWrap(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetOverflow( + const YGNodeRef node, + const YGOverflow overflow); +WIN_EXPORT YGOverflow YGNodeStyleGetOverflow(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetDisplay( + const YGNodeRef node, + const YGDisplay display); +WIN_EXPORT YGDisplay YGNodeStyleGetDisplay(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlex(const YGNodeRef node, const float flex); +WIN_EXPORT float YGNodeStyleGetFlex(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlexGrow( + const YGNodeRef node, + const float flexGrow); +WIN_EXPORT float YGNodeStyleGetFlexGrow(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlexShrink( + const YGNodeRef node, + const float flexShrink); +WIN_EXPORT float YGNodeStyleGetFlexShrink(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetFlexBasis( + const YGNodeRef node, + const float flexBasis); +WIN_EXPORT void YGNodeStyleSetFlexBasisPercent( + const YGNodeRef node, + const float flexBasis); +WIN_EXPORT void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetFlexBasis(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetPosition( + const YGNodeRef node, + const YGEdge edge, + const float position); +WIN_EXPORT void YGNodeStyleSetPositionPercent( + const YGNodeRef node, + const YGEdge edge, + const float position); +WIN_EXPORT YGValue +YGNodeStyleGetPosition(const YGNodeRef node, const YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetMargin( + const YGNodeRef node, + const YGEdge edge, + const float margin); +WIN_EXPORT void YGNodeStyleSetMarginPercent( + const YGNodeRef node, + const YGEdge edge, + const float margin); +WIN_EXPORT void YGNodeStyleSetMarginAuto( + const YGNodeRef node, + const YGEdge edge); +WIN_EXPORT YGValue +YGNodeStyleGetMargin(const YGNodeRef node, const YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetPadding( + const YGNodeRef node, + const YGEdge edge, + const float padding); +WIN_EXPORT void YGNodeStyleSetPaddingPercent( + const YGNodeRef node, + const YGEdge edge, + const float padding); +WIN_EXPORT YGValue +YGNodeStyleGetPadding(const YGNodeRef node, const YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetBorder( + const YGNodeRef node, + const YGEdge edge, + const float border); +WIN_EXPORT float YGNodeStyleGetBorder(const YGNodeRef node, const YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetWidth(const YGNodeRef node, const float width); +WIN_EXPORT void YGNodeStyleSetWidthPercent( + const YGNodeRef node, + const float width); +WIN_EXPORT void YGNodeStyleSetWidthAuto(const YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetWidth(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetHeight(const YGNodeRef node, const float height); +WIN_EXPORT void YGNodeStyleSetHeightPercent( + const YGNodeRef node, + const float height); +WIN_EXPORT void YGNodeStyleSetHeightAuto(const YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetHeight(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetMinWidth( + const YGNodeRef node, + const float minWidth); +WIN_EXPORT void YGNodeStyleSetMinWidthPercent( + const YGNodeRef node, + const float minWidth); +WIN_EXPORT YGValue YGNodeStyleGetMinWidth(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetMinHeight( + const YGNodeRef node, + const float minHeight); +WIN_EXPORT void YGNodeStyleSetMinHeightPercent( + const YGNodeRef node, + const float minHeight); +WIN_EXPORT YGValue YGNodeStyleGetMinHeight(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetMaxWidth( + const YGNodeRef node, + const float maxWidth); +WIN_EXPORT void YGNodeStyleSetMaxWidthPercent( + const YGNodeRef node, + const float maxWidth); +WIN_EXPORT YGValue YGNodeStyleGetMaxWidth(const YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetMaxHeight( + const YGNodeRef node, + const float maxHeight); +WIN_EXPORT void YGNodeStyleSetMaxHeightPercent( + const YGNodeRef node, + const float maxHeight); +WIN_EXPORT YGValue YGNodeStyleGetMaxHeight(const YGNodeRef node); + +// Yoga specific properties, not compatible with flexbox specification Aspect +// ratio control the size of the undefined dimension of a node. Aspect ratio is +// encoded as a floating point value width/height. e.g. A value of 2 leads to a +// node with a width twice the size of its height while a value of 0.5 gives the +// opposite effect. +// +// - On a node with a set width/height aspect ratio control the size of the +// unset dimension +// - On a node with a set flex basis aspect ratio controls the size of the node +// in the cross axis if unset +// - On a node with a measure function aspect ratio works as though the measure +// function measures the flex basis +// - On a node with flex grow/shrink aspect ratio controls the size of the node +// in the cross axis if unset +// - Aspect ratio takes min/max dimensions into account +WIN_EXPORT void YGNodeStyleSetAspectRatio( + const YGNodeRef node, + const float aspectRatio); +WIN_EXPORT float YGNodeStyleGetAspectRatio(const YGNodeRef node); + +WIN_EXPORT float YGNodeLayoutGetLeft(const YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetTop(const YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetRight(const YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetBottom(const YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetWidth(const YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetHeight(const YGNodeRef node); +WIN_EXPORT YGDirection YGNodeLayoutGetDirection(const YGNodeRef node); +WIN_EXPORT bool YGNodeLayoutGetHadOverflow(const YGNodeRef node); +bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(const YGNodeRef node); + +// Get the computed values for these nodes after performing layout. If they were +// set using point values then the returned value will be the same as +// YGNodeStyleGetXXX. However if they were set using a percentage value then the +// returned value is the computed value used during layout. +WIN_EXPORT float YGNodeLayoutGetMargin(const YGNodeRef node, const YGEdge edge); +WIN_EXPORT float YGNodeLayoutGetBorder(const YGNodeRef node, const YGEdge edge); +WIN_EXPORT float YGNodeLayoutGetPadding( + const YGNodeRef node, + const YGEdge edge); + +WIN_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger); +WIN_EXPORT void YGAssert(const bool condition, const char* message); +WIN_EXPORT void YGAssertWithNode( + const YGNodeRef node, + const bool condition, + const char* message); +WIN_EXPORT void YGAssertWithConfig( + const YGConfigRef config, + const bool condition, + const char* message); +// Set this to number of pixels in 1 point to round calculation results If you +// want to avoid rounding - set PointScaleFactor to 0 +WIN_EXPORT void YGConfigSetPointScaleFactor( + const YGConfigRef config, + const float pixelsInPoint); +void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour( + const YGConfigRef config, + const bool shouldDiffLayout); + +// Yoga previously had an error where containers would take the maximum space +// possible instead of the minimum like they are supposed to. In practice this +// resulted in implicit behaviour similar to align-self: stretch; Because this +// was such a long-standing bug we must allow legacy users to switch back to +// this behaviour. +WIN_EXPORT void YGConfigSetUseLegacyStretchBehaviour( + const YGConfigRef config, + const bool useLegacyStretchBehaviour); + +// YGConfig +WIN_EXPORT YGConfigRef YGConfigNew(void); +WIN_EXPORT void YGConfigFree(const YGConfigRef config); +WIN_EXPORT void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src); +WIN_EXPORT int32_t YGConfigGetInstanceCount(void); + +WIN_EXPORT void YGConfigSetExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled); +WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature); + +// Using the web defaults is the prefered configuration for new projects. Usage +// of non web defaults should be considered as legacy. +WIN_EXPORT void YGConfigSetUseWebDefaults( + const YGConfigRef config, + const bool enabled); +WIN_EXPORT bool YGConfigGetUseWebDefaults(const YGConfigRef config); + +WIN_EXPORT void YGConfigSetCloneNodeFunc( + const YGConfigRef config, + const YGCloneNodeFunc 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 float YGRoundValueToPixelGrid( + const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor); + +YG_EXTERN_C_END + +#ifdef __cplusplus + +#include +#include + +// Calls f on each node in the tree including the given node argument. +extern void YGTraversePreOrder( + YGNodeRef const node, + std::function&& f); + +extern void YGNodeSetChildren( + YGNodeRef const owner, + const std::vector& children); + +#endif diff --git a/Sources/yoga/instrumentation.h b/Sources/yoga/instrumentation.h new file mode 100755 index 0000000..b8691c1 --- /dev/null +++ b/Sources/yoga/instrumentation.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "YGConfig.h" +#include "YGMarker.h" +#include "YGNode.h" + +namespace facebook { +namespace yoga { +namespace marker { + +template +class MarkerSection { +private: + using Data = detail::MarkerData; + +public: + MarkerSection(YGNodeRef node) : MarkerSection{node, node->getConfig()} {} + ~MarkerSection() { + if (endMarker_) { + endMarker_(MarkerType, node_, markerData(&data), userData_); + } + } + + typename Data::type data = {}; + + template + static Ret wrap( + YGNodeRef node, + Ret (YGNode::*method)(Args...), + Args... args) { + MarkerSection section{node}; + return (node->*method)(std::forward(args)...); + } + +private: + decltype(YGMarkerCallbacks{}.endMarker) endMarker_; + YGNodeRef node_; + void* userData_; + + MarkerSection(YGNodeRef node, YGConfigRef config) + : MarkerSection{node, config ? &config->markerCallbacks : nullptr} {} + MarkerSection(YGNodeRef node, YGMarkerCallbacks* callbacks) + : endMarker_{callbacks ? callbacks->endMarker : nullptr}, + node_{node}, + userData_{ + callbacks && callbacks->startMarker + ? callbacks->startMarker(MarkerType, node, markerData(&data)) + : nullptr} {} + + static YGMarkerData markerData(typename Data::type* d) { + YGMarkerData markerData = {}; + Data::get(markerData) = d; + return markerData; + } +}; + +} // namespace marker +} // namespace yoga +} // namespace facebook diff --git a/Sources/yoga/log.cpp b/Sources/yoga/log.cpp new file mode 100755 index 0000000..62b3d4f --- /dev/null +++ b/Sources/yoga/log.cpp @@ -0,0 +1,67 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +#include "log.h" + +#include "Yoga.h" +#include "YGConfig.h" +#include "YGNode.h" + +namespace facebook { +namespace yoga { +namespace detail { + +namespace { + +void vlog( + YGConfig* config, + YGNode* node, + YGLogLevel level, + void* context, + const char* format, + va_list args) { + YGConfig* logConfig = config != nullptr ? config : YGConfigGetDefault(); + logConfig->log(logConfig, node, level, context, format, args); + + if (level == YGLogLevelFatal) { + abort(); + } +} +} // namespace + +void Log::log( + YGNode* node, + YGLogLevel level, + void* context, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog( + node == nullptr ? nullptr : node->getConfig(), + node, + level, + context, + format, + args); + va_end(args); +} + +void Log::log( + YGConfig* config, + YGLogLevel level, + void* context, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog(config, nullptr, level, context, format, args); + va_end(args); +} + +} // namespace detail +} // namespace yoga +} // namespace facebook diff --git a/Sources/yoga/log.h b/Sources/yoga/log.h new file mode 100755 index 0000000..f25ee1a --- /dev/null +++ b/Sources/yoga/log.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "YGEnums.h" + +struct YGNode; +struct YGConfig; + +namespace facebook { +namespace yoga { + +namespace detail { + +struct Log { + static void log( + YGNode* node, + YGLogLevel level, + void*, + const char* message, + ...) noexcept; + + static void log( + YGConfig* config, + YGLogLevel level, + void*, + const char* format, + ...) noexcept; +}; + +} // namespace detail +} // namespace yoga +} // namespace facebook