Yoga.cpp 151 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the LICENSE
  5. * file in the root directory of this source tree.
  6. */
  7. #include "Yoga.h"
  8. #include "log.h"
  9. #include <float.h>
  10. #include <string.h>
  11. #include <algorithm>
  12. #include "Utils.h"
  13. #include "YGNode.h"
  14. #include "YGNodePrint.h"
  15. #include "Yoga-internal.h"
  16. #include "instrumentation.h"
  17. #ifdef _MSC_VER
  18. #include <float.h>
  19. /* define fmaxf if < VC12 */
  20. #if _MSC_VER < 1800
  21. __forceinline const float fmaxf(const float a, const float b) {
  22. return (a > b) ? a : b;
  23. }
  24. #endif
  25. #endif
  26. using namespace facebook::yoga;
  27. using detail::Log;
  28. namespace {
  29. size_t usedMeasureCacheEntries = YG_MAX_CACHED_RESULT_COUNT;
  30. }
  31. void YGSetUsedCachedEntries(size_t n) {
  32. usedMeasureCacheEntries =
  33. n == 0 || n > YG_MAX_CACHED_RESULT_COUNT ? YG_MAX_CACHED_RESULT_COUNT : n;
  34. }
  35. #ifdef ANDROID
  36. static int YGAndroidLog(
  37. const YGConfigRef config,
  38. const YGNodeRef node,
  39. YGLogLevel level,
  40. const char* format,
  41. va_list args);
  42. #else
  43. static int YGDefaultLog(
  44. const YGConfigRef config,
  45. const YGNodeRef node,
  46. YGLogLevel level,
  47. const char* format,
  48. va_list args);
  49. #endif
  50. #ifdef ANDROID
  51. #include <android/log.h>
  52. static int YGAndroidLog(
  53. const YGConfigRef config,
  54. const YGNodeRef node,
  55. YGLogLevel level,
  56. const char* format,
  57. va_list args) {
  58. int androidLevel = YGLogLevelDebug;
  59. switch (level) {
  60. case YGLogLevelFatal:
  61. androidLevel = ANDROID_LOG_FATAL;
  62. break;
  63. case YGLogLevelError:
  64. androidLevel = ANDROID_LOG_ERROR;
  65. break;
  66. case YGLogLevelWarn:
  67. androidLevel = ANDROID_LOG_WARN;
  68. break;
  69. case YGLogLevelInfo:
  70. androidLevel = ANDROID_LOG_INFO;
  71. break;
  72. case YGLogLevelDebug:
  73. androidLevel = ANDROID_LOG_DEBUG;
  74. break;
  75. case YGLogLevelVerbose:
  76. androidLevel = ANDROID_LOG_VERBOSE;
  77. break;
  78. }
  79. const int result = __android_log_vprint(androidLevel, "yoga", format, args);
  80. return result;
  81. }
  82. #else
  83. #define YG_UNUSED(x) (void) (x);
  84. static int YGDefaultLog(
  85. const YGConfigRef config,
  86. const YGNodeRef node,
  87. YGLogLevel level,
  88. const char* format,
  89. va_list args) {
  90. YG_UNUSED(config);
  91. YG_UNUSED(node);
  92. switch (level) {
  93. case YGLogLevelError:
  94. case YGLogLevelFatal:
  95. return vfprintf(stderr, format, args);
  96. case YGLogLevelWarn:
  97. case YGLogLevelInfo:
  98. case YGLogLevelDebug:
  99. case YGLogLevelVerbose:
  100. default:
  101. return vprintf(format, args);
  102. }
  103. }
  104. #undef YG_UNUSED
  105. #endif
  106. bool YGFloatIsUndefined(const float value) {
  107. return facebook::yoga::isUndefined(value);
  108. }
  109. detail::CompactValue YGComputedEdgeValue(
  110. const YGStyle::Edges& edges,
  111. YGEdge edge,
  112. detail::CompactValue defaultValue) {
  113. if (!edges[edge].isUndefined()) {
  114. return edges[edge];
  115. }
  116. if ((edge == YGEdgeTop || edge == YGEdgeBottom) &&
  117. !edges[YGEdgeVertical].isUndefined()) {
  118. return edges[YGEdgeVertical];
  119. }
  120. if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart ||
  121. edge == YGEdgeEnd) &&
  122. !edges[YGEdgeHorizontal].isUndefined()) {
  123. return edges[YGEdgeHorizontal];
  124. }
  125. if (!edges[YGEdgeAll].isUndefined()) {
  126. return edges[YGEdgeAll];
  127. }
  128. if (edge == YGEdgeStart || edge == YGEdgeEnd) {
  129. return detail::CompactValue::ofUndefined();
  130. }
  131. return defaultValue;
  132. }
  133. void* YGNodeGetContext(YGNodeRef node) {
  134. return node->getContext();
  135. }
  136. void YGNodeSetContext(YGNodeRef node, void* context) {
  137. return node->setContext(context);
  138. }
  139. bool YGNodeHasMeasureFunc(YGNodeRef node) {
  140. return node->hasMeasureFunc();
  141. }
  142. void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
  143. node->setMeasureFunc(measureFunc);
  144. }
  145. bool YGNodeHasBaselineFunc(YGNodeRef node) {
  146. return node->hasBaselineFunc();
  147. }
  148. void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) {
  149. node->setBaselineFunc(baselineFunc);
  150. }
  151. YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node) {
  152. return node->getDirtied();
  153. }
  154. void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) {
  155. node->setDirtiedFunc(dirtiedFunc);
  156. }
  157. void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
  158. node->setPrintFunc(printFunc);
  159. }
  160. bool YGNodeGetHasNewLayout(YGNodeRef node) {
  161. return node->getHasNewLayout();
  162. }
  163. void YGConfigSetPrintTreeFlag(YGConfigRef config, bool enabled) {
  164. config->printTree = enabled;
  165. }
  166. void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
  167. node->setHasNewLayout(hasNewLayout);
  168. }
  169. YGNodeType YGNodeGetNodeType(YGNodeRef node) {
  170. return node->getNodeType();
  171. }
  172. void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
  173. return node->setNodeType(nodeType);
  174. }
  175. bool YGNodeIsDirty(YGNodeRef node) {
  176. return node->isDirty();
  177. }
  178. bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) {
  179. return node->didUseLegacyFlag();
  180. }
  181. void YGNodeMarkDirtyAndPropogateToDescendants(const YGNodeRef node) {
  182. return node->markDirtyAndPropogateDownwards();
  183. }
  184. int32_t gNodeInstanceCount = 0;
  185. int32_t gConfigInstanceCount = 0;
  186. WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
  187. const YGNodeRef node = new YGNode();
  188. YGAssertWithConfig(
  189. config, node != nullptr, "Could not allocate memory for node");
  190. gNodeInstanceCount++;
  191. if (config->useWebDefaults) {
  192. node->setStyleFlexDirection(YGFlexDirectionRow);
  193. node->setStyleAlignContent(YGAlignStretch);
  194. }
  195. node->setConfig(config);
  196. return node;
  197. }
  198. YGConfigRef YGConfigGetDefault() {
  199. static YGConfigRef defaultConfig = YGConfigNew();
  200. return defaultConfig;
  201. }
  202. YGNodeRef YGNodeNew(void) {
  203. return YGNodeNewWithConfig(YGConfigGetDefault());
  204. }
  205. YGNodeRef YGNodeClone(YGNodeRef oldNode) {
  206. YGNodeRef node = new YGNode(*oldNode);
  207. YGAssertWithConfig(
  208. oldNode->getConfig(),
  209. node != nullptr,
  210. "Could not allocate memory for node");
  211. gNodeInstanceCount++;
  212. node->setOwner(nullptr);
  213. return node;
  214. }
  215. static YGConfigRef YGConfigClone(const YGConfig& oldConfig) {
  216. const YGConfigRef config = new YGConfig(oldConfig);
  217. YGAssert(config != nullptr, "Could not allocate memory for config");
  218. if (config == nullptr) {
  219. abort();
  220. }
  221. gConfigInstanceCount++;
  222. return config;
  223. }
  224. static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) {
  225. YGNodeRef node = YGNodeClone(oldNode);
  226. YGVector vec = YGVector();
  227. vec.reserve(oldNode->getChildren().size());
  228. YGNodeRef childNode = nullptr;
  229. for (auto* item : oldNode->getChildren()) {
  230. childNode = YGNodeDeepClone(item);
  231. childNode->setOwner(node);
  232. vec.push_back(childNode);
  233. }
  234. node->setChildren(vec);
  235. if (oldNode->getConfig() != nullptr) {
  236. node->setConfig(YGConfigClone(*(oldNode->getConfig())));
  237. }
  238. return node;
  239. }
  240. void YGNodeFree(const YGNodeRef node) {
  241. if (YGNodeRef owner = node->getOwner()) {
  242. owner->removeChild(node);
  243. node->setOwner(nullptr);
  244. }
  245. const uint32_t childCount = YGNodeGetChildCount(node);
  246. for (uint32_t i = 0; i < childCount; i++) {
  247. const YGNodeRef child = YGNodeGetChild(node, i);
  248. child->setOwner(nullptr);
  249. }
  250. node->clearChildren();
  251. delete node;
  252. gNodeInstanceCount--;
  253. }
  254. static void YGConfigFreeRecursive(const YGNodeRef root) {
  255. if (root->getConfig() != nullptr) {
  256. gConfigInstanceCount--;
  257. delete root->getConfig();
  258. }
  259. // Delete configs recursively for childrens
  260. for (auto* child : root->getChildren()) {
  261. YGConfigFreeRecursive(child);
  262. }
  263. }
  264. void YGNodeFreeRecursiveWithCleanupFunc(
  265. const YGNodeRef root,
  266. YGNodeCleanupFunc cleanup) {
  267. uint32_t skipped = 0;
  268. while (YGNodeGetChildCount(root) > skipped) {
  269. const YGNodeRef child = YGNodeGetChild(root, skipped);
  270. if (child->getOwner() != root) {
  271. // Don't free shared nodes that we don't own.
  272. skipped += 1;
  273. } else {
  274. YGNodeRemoveChild(root, child);
  275. YGNodeFreeRecursive(child);
  276. }
  277. }
  278. if (cleanup != nullptr) {
  279. cleanup(root);
  280. }
  281. YGNodeFree(root);
  282. }
  283. void YGNodeFreeRecursive(const YGNodeRef root) {
  284. return YGNodeFreeRecursiveWithCleanupFunc(root, nullptr);
  285. }
  286. void YGNodeReset(YGNodeRef node) {
  287. node->reset();
  288. }
  289. int32_t YGNodeGetInstanceCount(void) {
  290. return gNodeInstanceCount;
  291. }
  292. int32_t YGConfigGetInstanceCount(void) {
  293. return gConfigInstanceCount;
  294. }
  295. YGConfigRef YGConfigNew(void) {
  296. #ifdef ANDROID
  297. const YGConfigRef config = new YGConfig(YGAndroidLog);
  298. #else
  299. const YGConfigRef config = new YGConfig(YGDefaultLog);
  300. #endif
  301. gConfigInstanceCount++;
  302. return config;
  303. }
  304. void YGConfigFree(const YGConfigRef config) {
  305. delete config;
  306. gConfigInstanceCount--;
  307. }
  308. void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) {
  309. memcpy(dest, src, sizeof(YGConfig));
  310. }
  311. void YGNodeSetIsReferenceBaseline(YGNodeRef node, bool isReferenceBaseline) {
  312. if (node->isReferenceBaseline() != isReferenceBaseline) {
  313. node->setIsReferenceBaseline(isReferenceBaseline);
  314. node->markDirtyAndPropogate();
  315. }
  316. }
  317. bool YGNodeIsReferenceBaseline(YGNodeRef node) {
  318. return node->isReferenceBaseline();
  319. }
  320. void YGNodeInsertChild(
  321. const YGNodeRef owner,
  322. const YGNodeRef child,
  323. const uint32_t index) {
  324. YGAssertWithNode(
  325. owner,
  326. child->getOwner() == nullptr,
  327. "Child already has a owner, it must be removed first.");
  328. YGAssertWithNode(
  329. owner,
  330. !owner->hasMeasureFunc(),
  331. "Cannot add child: Nodes with measure functions cannot have children.");
  332. owner->insertChild(child, index);
  333. child->setOwner(owner);
  334. owner->markDirtyAndPropogate();
  335. }
  336. void YGNodeRemoveChild(const YGNodeRef owner, const YGNodeRef excludedChild) {
  337. if (YGNodeGetChildCount(owner) == 0) {
  338. // This is an empty set. Nothing to remove.
  339. return;
  340. }
  341. // Children may be shared between parents, which is indicated by not having an
  342. // owner. We only want to reset the child completely if it is owned
  343. // exclusively by one node.
  344. auto childOwner = excludedChild->getOwner();
  345. if (owner->removeChild(excludedChild)) {
  346. if (owner == childOwner) {
  347. excludedChild->setLayout({}); // layout is no longer valid
  348. excludedChild->setOwner(nullptr);
  349. }
  350. owner->markDirtyAndPropogate();
  351. }
  352. }
  353. void YGNodeRemoveAllChildren(const YGNodeRef owner) {
  354. const uint32_t childCount = YGNodeGetChildCount(owner);
  355. if (childCount == 0) {
  356. // This is an empty set already. Nothing to do.
  357. return;
  358. }
  359. const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
  360. if (firstChild->getOwner() == owner) {
  361. // If the first child has this node as its owner, we assume that this child
  362. // set is unique.
  363. for (uint32_t i = 0; i < childCount; i++) {
  364. const YGNodeRef oldChild = YGNodeGetChild(owner, i);
  365. oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid
  366. oldChild->setOwner(nullptr);
  367. }
  368. owner->clearChildren();
  369. owner->markDirtyAndPropogate();
  370. return;
  371. }
  372. // Otherwise, we are not the owner of the child set. We don't have to do
  373. // anything to clear it.
  374. owner->setChildren(YGVector());
  375. owner->markDirtyAndPropogate();
  376. }
  377. static void YGNodeSetChildrenInternal(
  378. YGNodeRef const owner,
  379. const std::vector<YGNodeRef>& children) {
  380. if (!owner) {
  381. return;
  382. }
  383. if (children.size() == 0) {
  384. if (YGNodeGetChildCount(owner) > 0) {
  385. for (YGNodeRef const child : owner->getChildren()) {
  386. child->setLayout(YGLayout());
  387. child->setOwner(nullptr);
  388. }
  389. owner->setChildren(YGVector());
  390. owner->markDirtyAndPropogate();
  391. }
  392. } else {
  393. if (YGNodeGetChildCount(owner) > 0) {
  394. for (YGNodeRef const oldChild : owner->getChildren()) {
  395. // Our new children may have nodes in common with the old children. We
  396. // don't reset these common nodes.
  397. if (std::find(children.begin(), children.end(), oldChild) ==
  398. children.end()) {
  399. oldChild->setLayout(YGLayout());
  400. oldChild->setOwner(nullptr);
  401. }
  402. }
  403. }
  404. owner->setChildren(children);
  405. for (YGNodeRef child : children) {
  406. child->setOwner(owner);
  407. }
  408. owner->markDirtyAndPropogate();
  409. }
  410. }
  411. void YGNodeSetChildren(
  412. YGNodeRef const owner,
  413. const YGNodeRef c[],
  414. const uint32_t count) {
  415. const YGVector children = {c, c + count};
  416. YGNodeSetChildrenInternal(owner, children);
  417. }
  418. void YGNodeSetChildren(
  419. YGNodeRef const owner,
  420. const std::vector<YGNodeRef>& children) {
  421. YGNodeSetChildrenInternal(owner, children);
  422. }
  423. YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) {
  424. if (index < node->getChildren().size()) {
  425. return node->getChild(index);
  426. }
  427. return nullptr;
  428. }
  429. uint32_t YGNodeGetChildCount(const YGNodeRef node) {
  430. return static_cast<uint32_t>(node->getChildren().size());
  431. }
  432. YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
  433. return node->getOwner();
  434. }
  435. YGNodeRef YGNodeGetParent(const YGNodeRef node) {
  436. return node->getOwner();
  437. }
  438. void YGNodeMarkDirty(const YGNodeRef node) {
  439. YGAssertWithNode(
  440. node,
  441. node->hasMeasureFunc(),
  442. "Only leaf nodes with custom measure functions"
  443. "should manually mark themselves as dirty");
  444. node->markDirtyAndPropogate();
  445. }
  446. void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) {
  447. if (!(dstNode->getStyle() == srcNode->getStyle())) {
  448. dstNode->setStyle(srcNode->getStyle());
  449. dstNode->markDirtyAndPropogate();
  450. }
  451. }
  452. float YGNodeStyleGetFlexGrow(const YGNodeRef node) {
  453. return node->getStyle().flexGrow.isUndefined()
  454. ? kDefaultFlexGrow
  455. : node->getStyle().flexGrow.unwrap();
  456. }
  457. float YGNodeStyleGetFlexShrink(const YGNodeRef node) {
  458. return node->getStyle().flexShrink.isUndefined()
  459. ? (node->getConfig()->useWebDefaults ? kWebDefaultFlexShrink
  460. : kDefaultFlexShrink)
  461. : node->getStyle().flexShrink.unwrap();
  462. }
  463. namespace {
  464. struct Value {
  465. template <YGUnit U>
  466. static detail::CompactValue create(float value) {
  467. return detail::CompactValue::ofMaybe<U>(value);
  468. }
  469. };
  470. template <>
  471. inline detail::CompactValue Value::create<YGUnitUndefined>(float) {
  472. return detail::CompactValue::ofUndefined();
  473. }
  474. template <>
  475. inline detail::CompactValue Value::create<YGUnitAuto>(float) {
  476. return detail::CompactValue::ofAuto();
  477. }
  478. template <YGStyle::Dimensions YGStyle::*P>
  479. struct DimensionProp {
  480. template <YGDimension idx>
  481. static YGValue get(YGNodeRef node) {
  482. YGValue value = (node->getStyle().*P)[idx];
  483. if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) {
  484. value.value = YGUndefined;
  485. }
  486. return value;
  487. }
  488. template <YGDimension idx, YGUnit U>
  489. static void set(YGNodeRef node, float newValue) {
  490. auto value = Value::create<U>(newValue);
  491. if ((node->getStyle().*P)[idx] != value) {
  492. (node->getStyle().*P)[idx] = value;
  493. node->markDirtyAndPropogate();
  494. }
  495. }
  496. };
  497. } // namespace
  498. #define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \
  499. type, name, paramName, instanceName) \
  500. void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
  501. auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(paramName); \
  502. if (node->getStyle().instanceName != value) { \
  503. node->getStyle().instanceName = value; \
  504. node->markDirtyAndPropogate(); \
  505. } \
  506. } \
  507. \
  508. void YGNodeStyleSet##name##Percent( \
  509. const YGNodeRef node, const type paramName) { \
  510. auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(paramName); \
  511. if (node->getStyle().instanceName != value) { \
  512. node->getStyle().instanceName = value; \
  513. node->markDirtyAndPropogate(); \
  514. } \
  515. } \
  516. \
  517. void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \
  518. if (node->getStyle().instanceName != detail::CompactValue::ofAuto()) { \
  519. node->getStyle().instanceName = detail::CompactValue::ofAuto(); \
  520. node->markDirtyAndPropogate(); \
  521. } \
  522. }
  523. #define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL( \
  524. type, name, paramName, instanceName) \
  525. YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \
  526. float, name, paramName, instanceName) \
  527. \
  528. type YGNodeStyleGet##name(const YGNodeRef node) { \
  529. YGValue value = node->getStyle().instanceName; \
  530. if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
  531. value.value = YGUndefined; \
  532. } \
  533. return value; \
  534. }
  535. #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \
  536. void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \
  537. if (node->getStyle().instanceName[edge] != \
  538. detail::CompactValue::ofAuto()) { \
  539. node->getStyle().instanceName[edge] = detail::CompactValue::ofAuto(); \
  540. node->markDirtyAndPropogate(); \
  541. } \
  542. }
  543. #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL( \
  544. type, name, paramName, instanceName) \
  545. void YGNodeStyleSet##name( \
  546. const YGNodeRef node, const YGEdge edge, const float paramName) { \
  547. auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(paramName); \
  548. if (node->getStyle().instanceName[edge] != value) { \
  549. node->getStyle().instanceName[edge] = value; \
  550. node->markDirtyAndPropogate(); \
  551. } \
  552. } \
  553. \
  554. void YGNodeStyleSet##name##Percent( \
  555. const YGNodeRef node, const YGEdge edge, const float paramName) { \
  556. auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(paramName); \
  557. if (node->getStyle().instanceName[edge] != value) { \
  558. node->getStyle().instanceName[edge] = value; \
  559. node->markDirtyAndPropogate(); \
  560. } \
  561. } \
  562. \
  563. type YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \
  564. YGValue value = node->getStyle().instanceName[edge]; \
  565. if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
  566. value.value = YGUndefined; \
  567. } \
  568. return value; \
  569. }
  570. #define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \
  571. type YGNodeLayoutGet##name(const YGNodeRef node) { \
  572. return node->getLayout().instanceName; \
  573. }
  574. #define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \
  575. type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \
  576. YGAssertWithNode( \
  577. node, \
  578. edge <= YGEdgeEnd, \
  579. "Cannot get layout properties of multi-edge shorthands"); \
  580. \
  581. if (edge == YGEdgeLeft) { \
  582. if (node->getLayout().direction == YGDirectionRTL) { \
  583. return node->getLayout().instanceName[YGEdgeEnd]; \
  584. } else { \
  585. return node->getLayout().instanceName[YGEdgeStart]; \
  586. } \
  587. } \
  588. \
  589. if (edge == YGEdgeRight) { \
  590. if (node->getLayout().direction == YGDirectionRTL) { \
  591. return node->getLayout().instanceName[YGEdgeStart]; \
  592. } else { \
  593. return node->getLayout().instanceName[YGEdgeEnd]; \
  594. } \
  595. } \
  596. \
  597. return node->getLayout().instanceName[edge]; \
  598. }
  599. #define YG_NODE_STYLE_SET(node, property, value) \
  600. if (node->getStyle().property != value) { \
  601. node->getStyle().property = value; \
  602. node->markDirtyAndPropogate(); \
  603. }
  604. void YGNodeStyleSetDirection(const YGNodeRef node, const YGDirection value) {
  605. YG_NODE_STYLE_SET(node, direction, value);
  606. }
  607. YGDirection YGNodeStyleGetDirection(const YGNodeRef node) {
  608. return node->getStyle().direction;
  609. }
  610. void YGNodeStyleSetFlexDirection(
  611. const YGNodeRef node,
  612. const YGFlexDirection flexDirection) {
  613. YG_NODE_STYLE_SET(node, flexDirection, flexDirection);
  614. }
  615. YGFlexDirection YGNodeStyleGetFlexDirection(const YGNodeRef node) {
  616. return node->getStyle().flexDirection;
  617. }
  618. void YGNodeStyleSetJustifyContent(
  619. const YGNodeRef node,
  620. const YGJustify justifyContent) {
  621. YG_NODE_STYLE_SET(node, justifyContent, justifyContent);
  622. }
  623. YGJustify YGNodeStyleGetJustifyContent(const YGNodeRef node) {
  624. return node->getStyle().justifyContent;
  625. }
  626. void YGNodeStyleSetAlignContent(
  627. const YGNodeRef node,
  628. const YGAlign alignContent) {
  629. YG_NODE_STYLE_SET(node, alignContent, alignContent);
  630. }
  631. YGAlign YGNodeStyleGetAlignContent(const YGNodeRef node) {
  632. return node->getStyle().alignContent;
  633. }
  634. void YGNodeStyleSetAlignItems(const YGNodeRef node, const YGAlign alignItems) {
  635. YG_NODE_STYLE_SET(node, alignItems, alignItems);
  636. }
  637. YGAlign YGNodeStyleGetAlignItems(const YGNodeRef node) {
  638. return node->getStyle().alignItems;
  639. }
  640. void YGNodeStyleSetAlignSelf(const YGNodeRef node, const YGAlign alignSelf) {
  641. YG_NODE_STYLE_SET(node, alignSelf, alignSelf);
  642. }
  643. YGAlign YGNodeStyleGetAlignSelf(const YGNodeRef node) {
  644. return node->getStyle().alignSelf;
  645. }
  646. void YGNodeStyleSetPositionType(
  647. const YGNodeRef node,
  648. const YGPositionType positionType) {
  649. YG_NODE_STYLE_SET(node, positionType, positionType);
  650. }
  651. YGPositionType YGNodeStyleGetPositionType(const YGNodeRef node) {
  652. return node->getStyle().positionType;
  653. }
  654. void YGNodeStyleSetFlexWrap(const YGNodeRef node, const YGWrap flexWrap) {
  655. YG_NODE_STYLE_SET(node, flexWrap, flexWrap);
  656. }
  657. YGWrap YGNodeStyleGetFlexWrap(const YGNodeRef node) {
  658. return node->getStyle().flexWrap;
  659. }
  660. void YGNodeStyleSetOverflow(const YGNodeRef node, const YGOverflow overflow) {
  661. YG_NODE_STYLE_SET(node, overflow, overflow);
  662. }
  663. YGOverflow YGNodeStyleGetOverflow(const YGNodeRef node) {
  664. return node->getStyle().overflow;
  665. }
  666. void YGNodeStyleSetDisplay(const YGNodeRef node, const YGDisplay display) {
  667. YG_NODE_STYLE_SET(node, display, display);
  668. }
  669. YGDisplay YGNodeStyleGetDisplay(const YGNodeRef node) {
  670. return node->getStyle().display;
  671. }
  672. // TODO(T26792433): Change the API to accept YGFloatOptional.
  673. void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) {
  674. if (node->getStyle().flex != flex) {
  675. node->getStyle().flex =
  676. YGFloatIsUndefined(flex) ? YGFloatOptional() : YGFloatOptional(flex);
  677. node->markDirtyAndPropogate();
  678. }
  679. }
  680. // TODO(T26792433): Change the API to accept YGFloatOptional.
  681. float YGNodeStyleGetFlex(const YGNodeRef node) {
  682. return node->getStyle().flex.isUndefined() ? YGUndefined
  683. : node->getStyle().flex.unwrap();
  684. }
  685. // TODO(T26792433): Change the API to accept YGFloatOptional.
  686. void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) {
  687. if (node->getStyle().flexGrow != flexGrow) {
  688. node->getStyle().flexGrow = YGFloatIsUndefined(flexGrow)
  689. ? YGFloatOptional()
  690. : YGFloatOptional(flexGrow);
  691. node->markDirtyAndPropogate();
  692. }
  693. }
  694. // TODO(T26792433): Change the API to accept YGFloatOptional.
  695. void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) {
  696. if (node->getStyle().flexShrink != flexShrink) {
  697. node->getStyle().flexShrink = YGFloatIsUndefined(flexShrink)
  698. ? YGFloatOptional()
  699. : YGFloatOptional(flexShrink);
  700. node->markDirtyAndPropogate();
  701. }
  702. }
  703. YGValue YGNodeStyleGetFlexBasis(const YGNodeRef node) {
  704. YGValue flexBasis = node->getStyle().flexBasis;
  705. if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) {
  706. // TODO(T26792433): Get rid off the use of YGUndefined at client side
  707. flexBasis.value = YGUndefined;
  708. }
  709. return flexBasis;
  710. }
  711. void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) {
  712. auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(flexBasis);
  713. if (node->getStyle().flexBasis != value) {
  714. node->getStyle().flexBasis = value;
  715. node->markDirtyAndPropogate();
  716. }
  717. }
  718. void YGNodeStyleSetFlexBasisPercent(
  719. const YGNodeRef node,
  720. const float flexBasisPercent) {
  721. auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(flexBasisPercent);
  722. if (node->getStyle().flexBasis != value) {
  723. node->getStyle().flexBasis = value;
  724. node->markDirtyAndPropogate();
  725. }
  726. }
  727. void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) {
  728. if (node->getStyle().flexBasis != detail::CompactValue::ofAuto()) {
  729. node->getStyle().flexBasis = detail::CompactValue::ofAuto();
  730. node->markDirtyAndPropogate();
  731. }
  732. }
  733. YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position);
  734. YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin);
  735. YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin);
  736. YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding);
  737. // TODO(T26792433): Change the API to accept YGFloatOptional.
  738. void YGNodeStyleSetBorder(
  739. const YGNodeRef node,
  740. const YGEdge edge,
  741. const float border) {
  742. auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(border);
  743. if (node->getStyle().border[edge] != value) {
  744. node->getStyle().border[edge] = value;
  745. node->markDirtyAndPropogate();
  746. }
  747. }
  748. float YGNodeStyleGetBorder(const YGNodeRef node, const YGEdge edge) {
  749. if (node->getStyle().border[edge].isUndefined() ||
  750. node->getStyle().border[edge].isAuto()) {
  751. // TODO(T26792433): Rather than returning YGUndefined, change the api to
  752. // return YGFloatOptional.
  753. return YGUndefined;
  754. }
  755. auto border = (YGValue) node->getStyle().border[edge];
  756. return border.value;
  757. }
  758. // Yoga specific properties, not compatible with flexbox specification
  759. // TODO(T26792433): Change the API to accept YGFloatOptional.
  760. float YGNodeStyleGetAspectRatio(const YGNodeRef node) {
  761. const YGFloatOptional op = node->getStyle().aspectRatio;
  762. return op.isUndefined() ? YGUndefined : op.unwrap();
  763. }
  764. // TODO(T26792433): Change the API to accept YGFloatOptional.
  765. void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) {
  766. if (node->getStyle().aspectRatio != aspectRatio) {
  767. node->getStyle().aspectRatio = YGFloatOptional(aspectRatio);
  768. node->markDirtyAndPropogate();
  769. }
  770. }
  771. YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(
  772. YGValue,
  773. Width,
  774. width,
  775. dimensions[YGDimensionWidth]);
  776. YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(
  777. YGValue,
  778. Height,
  779. height,
  780. dimensions[YGDimensionHeight]);
  781. void YGNodeStyleSetMinWidth(const YGNodeRef node, const float minWidth) {
  782. DimensionProp<&YGStyle::minDimensions>::set<YGDimensionWidth, YGUnitPoint>(
  783. node, minWidth);
  784. }
  785. void YGNodeStyleSetMinWidthPercent(const YGNodeRef node, const float minWidth) {
  786. DimensionProp<&YGStyle::minDimensions>::set<YGDimensionWidth, YGUnitPercent>(
  787. node, minWidth);
  788. }
  789. YGValue YGNodeStyleGetMinWidth(const YGNodeRef node) {
  790. return DimensionProp<&YGStyle::minDimensions>::get<YGDimensionWidth>(node);
  791. };
  792. void YGNodeStyleSetMinHeight(const YGNodeRef node, const float minHeight) {
  793. DimensionProp<&YGStyle::minDimensions>::set<YGDimensionHeight, YGUnitPoint>(
  794. node, minHeight);
  795. }
  796. void YGNodeStyleSetMinHeightPercent(
  797. const YGNodeRef node,
  798. const float minHeight) {
  799. DimensionProp<&YGStyle::minDimensions>::set<YGDimensionHeight, YGUnitPercent>(
  800. node, minHeight);
  801. }
  802. YGValue YGNodeStyleGetMinHeight(const YGNodeRef node) {
  803. return DimensionProp<&YGStyle::minDimensions>::get<YGDimensionHeight>(node);
  804. };
  805. void YGNodeStyleSetMaxWidth(const YGNodeRef node, const float maxWidth) {
  806. DimensionProp<&YGStyle::maxDimensions>::set<YGDimensionWidth, YGUnitPoint>(
  807. node, maxWidth);
  808. }
  809. void YGNodeStyleSetMaxWidthPercent(const YGNodeRef node, const float maxWidth) {
  810. DimensionProp<&YGStyle::maxDimensions>::set<YGDimensionWidth, YGUnitPercent>(
  811. node, maxWidth);
  812. }
  813. YGValue YGNodeStyleGetMaxWidth(const YGNodeRef node) {
  814. return DimensionProp<&YGStyle::maxDimensions>::get<YGDimensionWidth>(node);
  815. };
  816. void YGNodeStyleSetMaxHeight(const YGNodeRef node, const float maxHeight) {
  817. DimensionProp<&YGStyle::maxDimensions>::set<YGDimensionHeight, YGUnitPoint>(
  818. node, maxHeight);
  819. }
  820. void YGNodeStyleSetMaxHeightPercent(
  821. const YGNodeRef node,
  822. const float maxHeight) {
  823. DimensionProp<&YGStyle::maxDimensions>::set<YGDimensionHeight, YGUnitPercent>(
  824. node, maxHeight);
  825. }
  826. YGValue YGNodeStyleGetMaxHeight(const YGNodeRef node) {
  827. return DimensionProp<&YGStyle::maxDimensions>::get<YGDimensionHeight>(node);
  828. };
  829. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]);
  830. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]);
  831. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]);
  832. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]);
  833. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]);
  834. YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]);
  835. YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction);
  836. YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow);
  837. YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin);
  838. YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border);
  839. YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding);
  840. bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(const YGNodeRef node) {
  841. return node->getLayout().doesLegacyStretchFlagAffectsLayout;
  842. }
  843. uint32_t gCurrentGenerationCount = 0;
  844. bool YGLayoutNodeInternal(
  845. const YGNodeRef node,
  846. const float availableWidth,
  847. const float availableHeight,
  848. const YGDirection ownerDirection,
  849. const YGMeasureMode widthMeasureMode,
  850. const YGMeasureMode heightMeasureMode,
  851. const float ownerWidth,
  852. const float ownerHeight,
  853. const bool performLayout,
  854. const char* reason,
  855. const YGConfigRef config,
  856. YGMarkerLayoutData& layoutMarkerData,
  857. void* const layoutContext);
  858. #ifdef DEBUG
  859. static void YGNodePrintInternal(
  860. const YGNodeRef node,
  861. const YGPrintOptions options) {
  862. std::string str;
  863. facebook::yoga::YGNodeToString(str, node, options, 0);
  864. Log::log(node, YGLogLevelDebug, nullptr, str.c_str());
  865. }
  866. void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) {
  867. YGNodePrintInternal(node, options);
  868. }
  869. #endif
  870. const std::array<YGEdge, 4> leading = {
  871. {YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}};
  872. const std::array<YGEdge, 4> trailing = {
  873. {YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}};
  874. static const std::array<YGEdge, 4> pos = {{
  875. YGEdgeTop,
  876. YGEdgeBottom,
  877. YGEdgeLeft,
  878. YGEdgeRight,
  879. }};
  880. static const std::array<YGDimension, 4> dim = {
  881. {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}};
  882. static inline float YGNodePaddingAndBorderForAxis(
  883. const YGNodeRef node,
  884. const YGFlexDirection axis,
  885. const float widthSize) {
  886. return (node->getLeadingPaddingAndBorder(axis, widthSize) +
  887. node->getTrailingPaddingAndBorder(axis, widthSize))
  888. .unwrap();
  889. }
  890. static inline YGAlign YGNodeAlignItem(
  891. const YGNodeRef node,
  892. const YGNodeRef child) {
  893. const YGAlign align = child->getStyle().alignSelf == YGAlignAuto
  894. ? node->getStyle().alignItems
  895. : child->getStyle().alignSelf;
  896. if (align == YGAlignBaseline &&
  897. YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
  898. return YGAlignFlexStart;
  899. }
  900. return align;
  901. }
  902. static float YGBaseline(const YGNodeRef node, void* layoutContext) {
  903. if (node->hasBaselineFunc()) {
  904. const float baseline = marker::MarkerSection<YGMarkerBaselineFn>::wrap(
  905. node,
  906. &YGNode::baseline,
  907. node->getLayout().measuredDimensions[YGDimensionWidth],
  908. node->getLayout().measuredDimensions[YGDimensionHeight],
  909. layoutContext);
  910. YGAssertWithNode(
  911. node,
  912. !YGFloatIsUndefined(baseline),
  913. "Expect custom baseline function to not return NaN");
  914. return baseline;
  915. }
  916. YGNodeRef baselineChild = nullptr;
  917. const uint32_t childCount = YGNodeGetChildCount(node);
  918. for (uint32_t i = 0; i < childCount; i++) {
  919. const YGNodeRef child = YGNodeGetChild(node, i);
  920. if (child->getLineIndex() > 0) {
  921. break;
  922. }
  923. if (child->getStyle().positionType == YGPositionTypeAbsolute) {
  924. continue;
  925. }
  926. if (YGNodeAlignItem(node, child) == YGAlignBaseline ||
  927. child->isReferenceBaseline()) {
  928. baselineChild = child;
  929. break;
  930. }
  931. if (baselineChild == nullptr) {
  932. baselineChild = child;
  933. }
  934. }
  935. if (baselineChild == nullptr) {
  936. return node->getLayout().measuredDimensions[YGDimensionHeight];
  937. }
  938. const float baseline = YGBaseline(baselineChild, layoutContext);
  939. return baseline + baselineChild->getLayout().position[YGEdgeTop];
  940. }
  941. static bool YGIsBaselineLayout(const YGNodeRef node) {
  942. if (YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
  943. return false;
  944. }
  945. if (node->getStyle().alignItems == YGAlignBaseline) {
  946. return true;
  947. }
  948. const uint32_t childCount = YGNodeGetChildCount(node);
  949. for (uint32_t i = 0; i < childCount; i++) {
  950. const YGNodeRef child = YGNodeGetChild(node, i);
  951. if (child->getStyle().positionType == YGPositionTypeRelative &&
  952. child->getStyle().alignSelf == YGAlignBaseline) {
  953. return true;
  954. }
  955. }
  956. return false;
  957. }
  958. static inline float YGNodeDimWithMargin(
  959. const YGNodeRef node,
  960. const YGFlexDirection axis,
  961. const float widthSize) {
  962. return node->getLayout().measuredDimensions[dim[axis]] +
  963. (node->getLeadingMargin(axis, widthSize) +
  964. node->getTrailingMargin(axis, widthSize))
  965. .unwrap();
  966. }
  967. static inline bool YGNodeIsStyleDimDefined(
  968. const YGNodeRef node,
  969. const YGFlexDirection axis,
  970. const float ownerSize) {
  971. bool isUndefined =
  972. YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value);
  973. return !(
  974. node->getResolvedDimension(dim[axis]).unit == YGUnitAuto ||
  975. node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined ||
  976. (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint &&
  977. !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) ||
  978. (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent &&
  979. !isUndefined &&
  980. (node->getResolvedDimension(dim[axis]).value < 0.0f ||
  981. YGFloatIsUndefined(ownerSize))));
  982. }
  983. static inline bool YGNodeIsLayoutDimDefined(
  984. const YGNodeRef node,
  985. const YGFlexDirection axis) {
  986. const float value = node->getLayout().measuredDimensions[dim[axis]];
  987. return !YGFloatIsUndefined(value) && value >= 0.0f;
  988. }
  989. static YGFloatOptional YGNodeBoundAxisWithinMinAndMax(
  990. const YGNodeRef node,
  991. const YGFlexDirection axis,
  992. const YGFloatOptional value,
  993. const float axisSize) {
  994. YGFloatOptional min;
  995. YGFloatOptional max;
  996. if (YGFlexDirectionIsColumn(axis)) {
  997. min = YGResolveValue(
  998. node->getStyle().minDimensions[YGDimensionHeight], axisSize);
  999. max = YGResolveValue(
  1000. node->getStyle().maxDimensions[YGDimensionHeight], axisSize);
  1001. } else if (YGFlexDirectionIsRow(axis)) {
  1002. min = YGResolveValue(
  1003. node->getStyle().minDimensions[YGDimensionWidth], axisSize);
  1004. max = YGResolveValue(
  1005. node->getStyle().maxDimensions[YGDimensionWidth], axisSize);
  1006. }
  1007. if (max >= YGFloatOptional{0} && value > max) {
  1008. return max;
  1009. }
  1010. if (min >= YGFloatOptional{0} && value < min) {
  1011. return min;
  1012. }
  1013. return value;
  1014. }
  1015. // Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't
  1016. // go below the padding and border amount.
  1017. static inline float YGNodeBoundAxis(
  1018. const YGNodeRef node,
  1019. const YGFlexDirection axis,
  1020. const float value,
  1021. const float axisSize,
  1022. const float widthSize) {
  1023. return YGFloatMax(
  1024. YGNodeBoundAxisWithinMinAndMax(
  1025. node, axis, YGFloatOptional{value}, axisSize)
  1026. .unwrap(),
  1027. YGNodePaddingAndBorderForAxis(node, axis, widthSize));
  1028. }
  1029. static void YGNodeSetChildTrailingPosition(
  1030. const YGNodeRef node,
  1031. const YGNodeRef child,
  1032. const YGFlexDirection axis) {
  1033. const float size = child->getLayout().measuredDimensions[dim[axis]];
  1034. child->setLayoutPosition(
  1035. node->getLayout().measuredDimensions[dim[axis]] - size -
  1036. child->getLayout().position[pos[axis]],
  1037. trailing[axis]);
  1038. }
  1039. static void YGConstrainMaxSizeForMode(
  1040. const YGNodeRef node,
  1041. const enum YGFlexDirection axis,
  1042. const float ownerAxisSize,
  1043. const float ownerWidth,
  1044. YGMeasureMode* mode,
  1045. float* size) {
  1046. const YGFloatOptional maxSize =
  1047. YGResolveValue(node->getStyle().maxDimensions[dim[axis]], ownerAxisSize) +
  1048. YGFloatOptional(node->getMarginForAxis(axis, ownerWidth));
  1049. switch (*mode) {
  1050. case YGMeasureModeExactly:
  1051. case YGMeasureModeAtMost:
  1052. *size = (maxSize.isUndefined() || *size < maxSize.unwrap())
  1053. ? *size
  1054. : maxSize.unwrap();
  1055. break;
  1056. case YGMeasureModeUndefined:
  1057. if (!maxSize.isUndefined()) {
  1058. *mode = YGMeasureModeAtMost;
  1059. *size = maxSize.unwrap();
  1060. }
  1061. break;
  1062. }
  1063. }
  1064. static void YGNodeComputeFlexBasisForChild(
  1065. const YGNodeRef node,
  1066. const YGNodeRef child,
  1067. const float width,
  1068. const YGMeasureMode widthMode,
  1069. const float height,
  1070. const float ownerWidth,
  1071. const float ownerHeight,
  1072. const YGMeasureMode heightMode,
  1073. const YGDirection direction,
  1074. const YGConfigRef config,
  1075. YGMarkerLayoutData& layoutMarkerData,
  1076. void* const layoutContext) {
  1077. const YGFlexDirection mainAxis =
  1078. YGResolveFlexDirection(node->getStyle().flexDirection, direction);
  1079. const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
  1080. const float mainAxisSize = isMainAxisRow ? width : height;
  1081. const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
  1082. float childWidth;
  1083. float childHeight;
  1084. YGMeasureMode childWidthMeasureMode;
  1085. YGMeasureMode childHeightMeasureMode;
  1086. const YGFloatOptional resolvedFlexBasis =
  1087. YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize);
  1088. const bool isRowStyleDimDefined =
  1089. YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth);
  1090. const bool isColumnStyleDimDefined =
  1091. YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, ownerHeight);
  1092. if (!resolvedFlexBasis.isUndefined() && !YGFloatIsUndefined(mainAxisSize)) {
  1093. if (child->getLayout().computedFlexBasis.isUndefined() ||
  1094. (YGConfigIsExperimentalFeatureEnabled(
  1095. child->getConfig(), YGExperimentalFeatureWebFlexBasis) &&
  1096. child->getLayout().computedFlexBasisGeneration !=
  1097. gCurrentGenerationCount)) {
  1098. const YGFloatOptional paddingAndBorder = YGFloatOptional(
  1099. YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth));
  1100. child->setLayoutComputedFlexBasis(
  1101. YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder));
  1102. }
  1103. } else if (isMainAxisRow && isRowStyleDimDefined) {
  1104. // The width is definite, so use that as the flex basis.
  1105. const YGFloatOptional paddingAndBorder = YGFloatOptional(
  1106. YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth));
  1107. child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
  1108. YGResolveValue(
  1109. child->getResolvedDimension(YGDimensionWidth), ownerWidth),
  1110. paddingAndBorder));
  1111. } else if (!isMainAxisRow && isColumnStyleDimDefined) {
  1112. // The height is definite, so use that as the flex basis.
  1113. const YGFloatOptional paddingAndBorder =
  1114. YGFloatOptional(YGNodePaddingAndBorderForAxis(
  1115. child, YGFlexDirectionColumn, ownerWidth));
  1116. child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
  1117. YGResolveValue(
  1118. child->getResolvedDimension(YGDimensionHeight), ownerHeight),
  1119. paddingAndBorder));
  1120. } else {
  1121. // Compute the flex basis and hypothetical main size (i.e. the clamped flex
  1122. // basis).
  1123. childWidth = YGUndefined;
  1124. childHeight = YGUndefined;
  1125. childWidthMeasureMode = YGMeasureModeUndefined;
  1126. childHeightMeasureMode = YGMeasureModeUndefined;
  1127. auto marginRow =
  1128. child->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
  1129. auto marginColumn =
  1130. child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
  1131. if (isRowStyleDimDefined) {
  1132. childWidth =
  1133. YGResolveValue(
  1134. child->getResolvedDimension(YGDimensionWidth), ownerWidth)
  1135. .unwrap() +
  1136. marginRow;
  1137. childWidthMeasureMode = YGMeasureModeExactly;
  1138. }
  1139. if (isColumnStyleDimDefined) {
  1140. childHeight =
  1141. YGResolveValue(
  1142. child->getResolvedDimension(YGDimensionHeight), ownerHeight)
  1143. .unwrap() +
  1144. marginColumn;
  1145. childHeightMeasureMode = YGMeasureModeExactly;
  1146. }
  1147. // The W3C spec doesn't say anything about the 'overflow' property, but all
  1148. // major browsers appear to implement the following logic.
  1149. if ((!isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
  1150. node->getStyle().overflow != YGOverflowScroll) {
  1151. if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) {
  1152. childWidth = width;
  1153. childWidthMeasureMode = YGMeasureModeAtMost;
  1154. }
  1155. }
  1156. if ((isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
  1157. node->getStyle().overflow != YGOverflowScroll) {
  1158. if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) {
  1159. childHeight = height;
  1160. childHeightMeasureMode = YGMeasureModeAtMost;
  1161. }
  1162. }
  1163. if (!child->getStyle().aspectRatio.isUndefined()) {
  1164. if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) {
  1165. childHeight = marginColumn +
  1166. (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap();
  1167. childHeightMeasureMode = YGMeasureModeExactly;
  1168. } else if (
  1169. isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) {
  1170. childWidth = marginRow +
  1171. (childHeight - marginColumn) *
  1172. child->getStyle().aspectRatio.unwrap();
  1173. childWidthMeasureMode = YGMeasureModeExactly;
  1174. }
  1175. }
  1176. // If child has no defined size in the cross axis and is set to stretch, set
  1177. // the cross axis to be measured exactly with the available inner width
  1178. const bool hasExactWidth =
  1179. !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly;
  1180. const bool childWidthStretch =
  1181. YGNodeAlignItem(node, child) == YGAlignStretch &&
  1182. childWidthMeasureMode != YGMeasureModeExactly;
  1183. if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth &&
  1184. childWidthStretch) {
  1185. childWidth = width;
  1186. childWidthMeasureMode = YGMeasureModeExactly;
  1187. if (!child->getStyle().aspectRatio.isUndefined()) {
  1188. childHeight =
  1189. (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap();
  1190. childHeightMeasureMode = YGMeasureModeExactly;
  1191. }
  1192. }
  1193. const bool hasExactHeight =
  1194. !YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly;
  1195. const bool childHeightStretch =
  1196. YGNodeAlignItem(node, child) == YGAlignStretch &&
  1197. childHeightMeasureMode != YGMeasureModeExactly;
  1198. if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight &&
  1199. childHeightStretch) {
  1200. childHeight = height;
  1201. childHeightMeasureMode = YGMeasureModeExactly;
  1202. if (!child->getStyle().aspectRatio.isUndefined()) {
  1203. childWidth = (childHeight - marginColumn) *
  1204. child->getStyle().aspectRatio.unwrap();
  1205. childWidthMeasureMode = YGMeasureModeExactly;
  1206. }
  1207. }
  1208. YGConstrainMaxSizeForMode(
  1209. child,
  1210. YGFlexDirectionRow,
  1211. ownerWidth,
  1212. ownerWidth,
  1213. &childWidthMeasureMode,
  1214. &childWidth);
  1215. YGConstrainMaxSizeForMode(
  1216. child,
  1217. YGFlexDirectionColumn,
  1218. ownerHeight,
  1219. ownerWidth,
  1220. &childHeightMeasureMode,
  1221. &childHeight);
  1222. // Measure the child
  1223. YGLayoutNodeInternal(
  1224. child,
  1225. childWidth,
  1226. childHeight,
  1227. direction,
  1228. childWidthMeasureMode,
  1229. childHeightMeasureMode,
  1230. ownerWidth,
  1231. ownerHeight,
  1232. false,
  1233. "measure",
  1234. config,
  1235. layoutMarkerData,
  1236. layoutContext);
  1237. child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax(
  1238. child->getLayout().measuredDimensions[dim[mainAxis]],
  1239. YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth))));
  1240. }
  1241. child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
  1242. }
  1243. static void YGNodeAbsoluteLayoutChild(
  1244. const YGNodeRef node,
  1245. const YGNodeRef child,
  1246. const float width,
  1247. const YGMeasureMode widthMode,
  1248. const float height,
  1249. const YGDirection direction,
  1250. const YGConfigRef config,
  1251. YGMarkerLayoutData& layoutMarkerData,
  1252. void* const layoutContext) {
  1253. const YGFlexDirection mainAxis =
  1254. YGResolveFlexDirection(node->getStyle().flexDirection, direction);
  1255. const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
  1256. const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
  1257. float childWidth = YGUndefined;
  1258. float childHeight = YGUndefined;
  1259. YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined;
  1260. YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined;
  1261. auto marginRow = child->getMarginForAxis(YGFlexDirectionRow, width).unwrap();
  1262. auto marginColumn =
  1263. child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap();
  1264. if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) {
  1265. childWidth =
  1266. YGResolveValue(child->getResolvedDimension(YGDimensionWidth), width)
  1267. .unwrap() +
  1268. marginRow;
  1269. } else {
  1270. // If the child doesn't have a specified width, compute the width based on
  1271. // the left/right offsets if they're defined.
  1272. if (child->isLeadingPositionDefined(YGFlexDirectionRow) &&
  1273. child->isTrailingPosDefined(YGFlexDirectionRow)) {
  1274. childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] -
  1275. (node->getLeadingBorder(YGFlexDirectionRow) +
  1276. node->getTrailingBorder(YGFlexDirectionRow)) -
  1277. (child->getLeadingPosition(YGFlexDirectionRow, width) +
  1278. child->getTrailingPosition(YGFlexDirectionRow, width))
  1279. .unwrap();
  1280. childWidth =
  1281. YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width);
  1282. }
  1283. }
  1284. if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) {
  1285. childHeight =
  1286. YGResolveValue(child->getResolvedDimension(YGDimensionHeight), height)
  1287. .unwrap() +
  1288. marginColumn;
  1289. } else {
  1290. // If the child doesn't have a specified height, compute the height based on
  1291. // the top/bottom offsets if they're defined.
  1292. if (child->isLeadingPositionDefined(YGFlexDirectionColumn) &&
  1293. child->isTrailingPosDefined(YGFlexDirectionColumn)) {
  1294. childHeight = node->getLayout().measuredDimensions[YGDimensionHeight] -
  1295. (node->getLeadingBorder(YGFlexDirectionColumn) +
  1296. node->getTrailingBorder(YGFlexDirectionColumn)) -
  1297. (child->getLeadingPosition(YGFlexDirectionColumn, height) +
  1298. child->getTrailingPosition(YGFlexDirectionColumn, height))
  1299. .unwrap();
  1300. childHeight = YGNodeBoundAxis(
  1301. child, YGFlexDirectionColumn, childHeight, height, width);
  1302. }
  1303. }
  1304. // Exactly one dimension needs to be defined for us to be able to do aspect
  1305. // ratio calculation. One dimension being the anchor and the other being
  1306. // flexible.
  1307. if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) {
  1308. if (!child->getStyle().aspectRatio.isUndefined()) {
  1309. if (YGFloatIsUndefined(childWidth)) {
  1310. childWidth = marginRow +
  1311. (childHeight - marginColumn) *
  1312. child->getStyle().aspectRatio.unwrap();
  1313. } else if (YGFloatIsUndefined(childHeight)) {
  1314. childHeight = marginColumn +
  1315. (childWidth - marginRow) / child->getStyle().aspectRatio.unwrap();
  1316. }
  1317. }
  1318. }
  1319. // If we're still missing one or the other dimension, measure the content.
  1320. if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) {
  1321. childWidthMeasureMode = YGFloatIsUndefined(childWidth)
  1322. ? YGMeasureModeUndefined
  1323. : YGMeasureModeExactly;
  1324. childHeightMeasureMode = YGFloatIsUndefined(childHeight)
  1325. ? YGMeasureModeUndefined
  1326. : YGMeasureModeExactly;
  1327. // If the size of the owner is defined then try to constrain the absolute
  1328. // child to that size as well. This allows text within the absolute child to
  1329. // wrap to the size of its owner. This is the same behavior as many browsers
  1330. // implement.
  1331. if (!isMainAxisRow && YGFloatIsUndefined(childWidth) &&
  1332. widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) &&
  1333. width > 0) {
  1334. childWidth = width;
  1335. childWidthMeasureMode = YGMeasureModeAtMost;
  1336. }
  1337. YGLayoutNodeInternal(
  1338. child,
  1339. childWidth,
  1340. childHeight,
  1341. direction,
  1342. childWidthMeasureMode,
  1343. childHeightMeasureMode,
  1344. childWidth,
  1345. childHeight,
  1346. false,
  1347. "abs-measure",
  1348. config,
  1349. layoutMarkerData,
  1350. layoutContext);
  1351. childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] +
  1352. child->getMarginForAxis(YGFlexDirectionRow, width).unwrap();
  1353. childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] +
  1354. child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap();
  1355. }
  1356. YGLayoutNodeInternal(
  1357. child,
  1358. childWidth,
  1359. childHeight,
  1360. direction,
  1361. YGMeasureModeExactly,
  1362. YGMeasureModeExactly,
  1363. childWidth,
  1364. childHeight,
  1365. true,
  1366. "abs-layout",
  1367. config,
  1368. layoutMarkerData,
  1369. layoutContext);
  1370. if (child->isTrailingPosDefined(mainAxis) &&
  1371. !child->isLeadingPositionDefined(mainAxis)) {
  1372. child->setLayoutPosition(
  1373. node->getLayout().measuredDimensions[dim[mainAxis]] -
  1374. child->getLayout().measuredDimensions[dim[mainAxis]] -
  1375. node->getTrailingBorder(mainAxis) -
  1376. child->getTrailingMargin(mainAxis, width).unwrap() -
  1377. child->getTrailingPosition(mainAxis, isMainAxisRow ? width : height)
  1378. .unwrap(),
  1379. leading[mainAxis]);
  1380. } else if (
  1381. !child->isLeadingPositionDefined(mainAxis) &&
  1382. node->getStyle().justifyContent == YGJustifyCenter) {
  1383. child->setLayoutPosition(
  1384. (node->getLayout().measuredDimensions[dim[mainAxis]] -
  1385. child->getLayout().measuredDimensions[dim[mainAxis]]) /
  1386. 2.0f,
  1387. leading[mainAxis]);
  1388. } else if (
  1389. !child->isLeadingPositionDefined(mainAxis) &&
  1390. node->getStyle().justifyContent == YGJustifyFlexEnd) {
  1391. child->setLayoutPosition(
  1392. (node->getLayout().measuredDimensions[dim[mainAxis]] -
  1393. child->getLayout().measuredDimensions[dim[mainAxis]]),
  1394. leading[mainAxis]);
  1395. }
  1396. if (child->isTrailingPosDefined(crossAxis) &&
  1397. !child->isLeadingPositionDefined(crossAxis)) {
  1398. child->setLayoutPosition(
  1399. node->getLayout().measuredDimensions[dim[crossAxis]] -
  1400. child->getLayout().measuredDimensions[dim[crossAxis]] -
  1401. node->getTrailingBorder(crossAxis) -
  1402. child->getTrailingMargin(crossAxis, width).unwrap() -
  1403. child
  1404. ->getTrailingPosition(crossAxis, isMainAxisRow ? height : width)
  1405. .unwrap(),
  1406. leading[crossAxis]);
  1407. } else if (
  1408. !child->isLeadingPositionDefined(crossAxis) &&
  1409. YGNodeAlignItem(node, child) == YGAlignCenter) {
  1410. child->setLayoutPosition(
  1411. (node->getLayout().measuredDimensions[dim[crossAxis]] -
  1412. child->getLayout().measuredDimensions[dim[crossAxis]]) /
  1413. 2.0f,
  1414. leading[crossAxis]);
  1415. } else if (
  1416. !child->isLeadingPositionDefined(crossAxis) &&
  1417. ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^
  1418. (node->getStyle().flexWrap == YGWrapWrapReverse))) {
  1419. child->setLayoutPosition(
  1420. (node->getLayout().measuredDimensions[dim[crossAxis]] -
  1421. child->getLayout().measuredDimensions[dim[crossAxis]]),
  1422. leading[crossAxis]);
  1423. }
  1424. }
  1425. static void YGNodeWithMeasureFuncSetMeasuredDimensions(
  1426. const YGNodeRef node,
  1427. const float availableWidth,
  1428. const float availableHeight,
  1429. const YGMeasureMode widthMeasureMode,
  1430. const YGMeasureMode heightMeasureMode,
  1431. const float ownerWidth,
  1432. const float ownerHeight,
  1433. void* const layoutContext) {
  1434. YGAssertWithNode(
  1435. node,
  1436. node->hasMeasureFunc(),
  1437. "Expected node to have custom measure function");
  1438. const float paddingAndBorderAxisRow =
  1439. YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth);
  1440. const float paddingAndBorderAxisColumn = YGNodePaddingAndBorderForAxis(
  1441. node, YGFlexDirectionColumn, availableWidth);
  1442. const float marginAxisRow =
  1443. node->getMarginForAxis(YGFlexDirectionRow, availableWidth).unwrap();
  1444. const float marginAxisColumn =
  1445. node->getMarginForAxis(YGFlexDirectionColumn, availableWidth).unwrap();
  1446. // We want to make sure we don't call measure with negative size
  1447. const float innerWidth = YGFloatIsUndefined(availableWidth)
  1448. ? availableWidth
  1449. : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow);
  1450. const float innerHeight = YGFloatIsUndefined(availableHeight)
  1451. ? availableHeight
  1452. : YGFloatMax(
  1453. 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn);
  1454. if (widthMeasureMode == YGMeasureModeExactly &&
  1455. heightMeasureMode == YGMeasureModeExactly) {
  1456. // Don't bother sizing the text if both dimensions are already defined.
  1457. node->setLayoutMeasuredDimension(
  1458. YGNodeBoundAxis(
  1459. node,
  1460. YGFlexDirectionRow,
  1461. availableWidth - marginAxisRow,
  1462. ownerWidth,
  1463. ownerWidth),
  1464. YGDimensionWidth);
  1465. node->setLayoutMeasuredDimension(
  1466. YGNodeBoundAxis(
  1467. node,
  1468. YGFlexDirectionColumn,
  1469. availableHeight - marginAxisColumn,
  1470. ownerHeight,
  1471. ownerWidth),
  1472. YGDimensionHeight);
  1473. } else {
  1474. // Measure the text under the current constraints.
  1475. const YGSize measuredSize = marker::MarkerSection<YGMarkerMeasure>::wrap(
  1476. node,
  1477. &YGNode::measure,
  1478. innerWidth,
  1479. widthMeasureMode,
  1480. innerHeight,
  1481. heightMeasureMode,
  1482. layoutContext);
  1483. node->setLayoutMeasuredDimension(
  1484. YGNodeBoundAxis(
  1485. node,
  1486. YGFlexDirectionRow,
  1487. (widthMeasureMode == YGMeasureModeUndefined ||
  1488. widthMeasureMode == YGMeasureModeAtMost)
  1489. ? measuredSize.width + paddingAndBorderAxisRow
  1490. : availableWidth - marginAxisRow,
  1491. ownerWidth,
  1492. ownerWidth),
  1493. YGDimensionWidth);
  1494. node->setLayoutMeasuredDimension(
  1495. YGNodeBoundAxis(
  1496. node,
  1497. YGFlexDirectionColumn,
  1498. (heightMeasureMode == YGMeasureModeUndefined ||
  1499. heightMeasureMode == YGMeasureModeAtMost)
  1500. ? measuredSize.height + paddingAndBorderAxisColumn
  1501. : availableHeight - marginAxisColumn,
  1502. ownerHeight,
  1503. ownerWidth),
  1504. YGDimensionHeight);
  1505. }
  1506. }
  1507. // For nodes with no children, use the available values if they were provided,
  1508. // or the minimum size as indicated by the padding and border sizes.
  1509. static void YGNodeEmptyContainerSetMeasuredDimensions(
  1510. const YGNodeRef node,
  1511. const float availableWidth,
  1512. const float availableHeight,
  1513. const YGMeasureMode widthMeasureMode,
  1514. const YGMeasureMode heightMeasureMode,
  1515. const float ownerWidth,
  1516. const float ownerHeight) {
  1517. const float paddingAndBorderAxisRow =
  1518. YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth);
  1519. const float paddingAndBorderAxisColumn =
  1520. YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth);
  1521. const float marginAxisRow =
  1522. node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
  1523. const float marginAxisColumn =
  1524. node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
  1525. node->setLayoutMeasuredDimension(
  1526. YGNodeBoundAxis(
  1527. node,
  1528. YGFlexDirectionRow,
  1529. (widthMeasureMode == YGMeasureModeUndefined ||
  1530. widthMeasureMode == YGMeasureModeAtMost)
  1531. ? paddingAndBorderAxisRow
  1532. : availableWidth - marginAxisRow,
  1533. ownerWidth,
  1534. ownerWidth),
  1535. YGDimensionWidth);
  1536. node->setLayoutMeasuredDimension(
  1537. YGNodeBoundAxis(
  1538. node,
  1539. YGFlexDirectionColumn,
  1540. (heightMeasureMode == YGMeasureModeUndefined ||
  1541. heightMeasureMode == YGMeasureModeAtMost)
  1542. ? paddingAndBorderAxisColumn
  1543. : availableHeight - marginAxisColumn,
  1544. ownerHeight,
  1545. ownerWidth),
  1546. YGDimensionHeight);
  1547. }
  1548. static bool YGNodeFixedSizeSetMeasuredDimensions(
  1549. const YGNodeRef node,
  1550. const float availableWidth,
  1551. const float availableHeight,
  1552. const YGMeasureMode widthMeasureMode,
  1553. const YGMeasureMode heightMeasureMode,
  1554. const float ownerWidth,
  1555. const float ownerHeight) {
  1556. if ((!YGFloatIsUndefined(availableWidth) &&
  1557. widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) ||
  1558. (!YGFloatIsUndefined(availableHeight) &&
  1559. heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) ||
  1560. (widthMeasureMode == YGMeasureModeExactly &&
  1561. heightMeasureMode == YGMeasureModeExactly)) {
  1562. auto marginAxisColumn =
  1563. node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
  1564. auto marginAxisRow =
  1565. node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
  1566. node->setLayoutMeasuredDimension(
  1567. YGNodeBoundAxis(
  1568. node,
  1569. YGFlexDirectionRow,
  1570. YGFloatIsUndefined(availableWidth) ||
  1571. (widthMeasureMode == YGMeasureModeAtMost &&
  1572. availableWidth < 0.0f)
  1573. ? 0.0f
  1574. : availableWidth - marginAxisRow,
  1575. ownerWidth,
  1576. ownerWidth),
  1577. YGDimensionWidth);
  1578. node->setLayoutMeasuredDimension(
  1579. YGNodeBoundAxis(
  1580. node,
  1581. YGFlexDirectionColumn,
  1582. YGFloatIsUndefined(availableHeight) ||
  1583. (heightMeasureMode == YGMeasureModeAtMost &&
  1584. availableHeight < 0.0f)
  1585. ? 0.0f
  1586. : availableHeight - marginAxisColumn,
  1587. ownerHeight,
  1588. ownerWidth),
  1589. YGDimensionHeight);
  1590. return true;
  1591. }
  1592. return false;
  1593. }
  1594. static void YGZeroOutLayoutRecursivly(
  1595. const YGNodeRef node,
  1596. void* layoutContext) {
  1597. node->getLayout() = {};
  1598. node->setLayoutDimension(0, 0);
  1599. node->setLayoutDimension(0, 1);
  1600. node->setHasNewLayout(true);
  1601. node->iterChildrenAfterCloningIfNeeded(
  1602. YGZeroOutLayoutRecursivly, layoutContext);
  1603. }
  1604. static float YGNodeCalculateAvailableInnerDim(
  1605. const YGNodeRef node,
  1606. YGFlexDirection axis,
  1607. float availableDim,
  1608. float ownerDim) {
  1609. YGFlexDirection direction =
  1610. YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn;
  1611. YGDimension dimension =
  1612. YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight;
  1613. const float margin = node->getMarginForAxis(direction, ownerDim).unwrap();
  1614. const float paddingAndBorder =
  1615. YGNodePaddingAndBorderForAxis(node, direction, ownerDim);
  1616. float availableInnerDim = availableDim - margin - paddingAndBorder;
  1617. // Max dimension overrides predefined dimension value; Min dimension in turn
  1618. // overrides both of the above
  1619. if (!YGFloatIsUndefined(availableInnerDim)) {
  1620. // We want to make sure our available height does not violate min and max
  1621. // constraints
  1622. const YGFloatOptional minDimensionOptional =
  1623. YGResolveValue(node->getStyle().minDimensions[dimension], ownerDim);
  1624. const float minInnerDim = minDimensionOptional.isUndefined()
  1625. ? 0.0f
  1626. : minDimensionOptional.unwrap() - paddingAndBorder;
  1627. const YGFloatOptional maxDimensionOptional =
  1628. YGResolveValue(node->getStyle().maxDimensions[dimension], ownerDim);
  1629. const float maxInnerDim = maxDimensionOptional.isUndefined()
  1630. ? FLT_MAX
  1631. : maxDimensionOptional.unwrap() - paddingAndBorder;
  1632. availableInnerDim =
  1633. YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim);
  1634. }
  1635. return availableInnerDim;
  1636. }
  1637. static float YGNodeComputeFlexBasisForChildren(
  1638. const YGNodeRef node,
  1639. const float availableInnerWidth,
  1640. const float availableInnerHeight,
  1641. YGMeasureMode widthMeasureMode,
  1642. YGMeasureMode heightMeasureMode,
  1643. YGDirection direction,
  1644. YGFlexDirection mainAxis,
  1645. const YGConfigRef config,
  1646. bool performLayout,
  1647. YGMarkerLayoutData& layoutMarkerData,
  1648. void* const layoutContext) {
  1649. float totalOuterFlexBasis = 0.0f;
  1650. YGNodeRef singleFlexChild = nullptr;
  1651. YGVector children = node->getChildren();
  1652. YGMeasureMode measureModeMainDim =
  1653. YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode;
  1654. // If there is only one child with flexGrow + flexShrink it means we can set
  1655. // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the
  1656. // child to exactly match the remaining space
  1657. if (measureModeMainDim == YGMeasureModeExactly) {
  1658. for (auto child : children) {
  1659. if (child->isNodeFlexible()) {
  1660. if (singleFlexChild != nullptr ||
  1661. YGFloatsEqual(child->resolveFlexGrow(), 0.0f) ||
  1662. YGFloatsEqual(child->resolveFlexShrink(), 0.0f)) {
  1663. // There is already a flexible child, or this flexible child doesn't
  1664. // have flexGrow and flexShrink, abort
  1665. singleFlexChild = nullptr;
  1666. break;
  1667. } else {
  1668. singleFlexChild = child;
  1669. }
  1670. }
  1671. }
  1672. }
  1673. for (auto child : children) {
  1674. child->resolveDimension();
  1675. if (child->getStyle().display == YGDisplayNone) {
  1676. YGZeroOutLayoutRecursivly(child, layoutContext);
  1677. child->setHasNewLayout(true);
  1678. child->setDirty(false);
  1679. continue;
  1680. }
  1681. if (performLayout) {
  1682. // Set the initial position (relative to the owner).
  1683. const YGDirection childDirection = child->resolveDirection(direction);
  1684. const float mainDim = YGFlexDirectionIsRow(mainAxis)
  1685. ? availableInnerWidth
  1686. : availableInnerHeight;
  1687. const float crossDim = YGFlexDirectionIsRow(mainAxis)
  1688. ? availableInnerHeight
  1689. : availableInnerWidth;
  1690. child->setPosition(
  1691. childDirection, mainDim, crossDim, availableInnerWidth);
  1692. }
  1693. if (child->getStyle().positionType == YGPositionTypeAbsolute) {
  1694. continue;
  1695. }
  1696. if (child == singleFlexChild) {
  1697. child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
  1698. child->setLayoutComputedFlexBasis(YGFloatOptional(0));
  1699. } else {
  1700. YGNodeComputeFlexBasisForChild(
  1701. node,
  1702. child,
  1703. availableInnerWidth,
  1704. widthMeasureMode,
  1705. availableInnerHeight,
  1706. availableInnerWidth,
  1707. availableInnerHeight,
  1708. heightMeasureMode,
  1709. direction,
  1710. config,
  1711. layoutMarkerData,
  1712. layoutContext);
  1713. }
  1714. totalOuterFlexBasis +=
  1715. (child->getLayout().computedFlexBasis +
  1716. child->getMarginForAxis(mainAxis, availableInnerWidth))
  1717. .unwrap();
  1718. }
  1719. return totalOuterFlexBasis;
  1720. }
  1721. // This function assumes that all the children of node have their
  1722. // computedFlexBasis properly computed(To do this use
  1723. // YGNodeComputeFlexBasisForChildren function). This function calculates
  1724. // YGCollectFlexItemsRowMeasurement
  1725. static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
  1726. const YGNodeRef& node,
  1727. const YGDirection ownerDirection,
  1728. const float mainAxisownerSize,
  1729. const float availableInnerWidth,
  1730. const float availableInnerMainDim,
  1731. const uint32_t startOfLineIndex,
  1732. const uint32_t lineCount) {
  1733. YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {};
  1734. flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
  1735. float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
  1736. const YGFlexDirection mainAxis = YGResolveFlexDirection(
  1737. node->getStyle().flexDirection, node->resolveDirection(ownerDirection));
  1738. const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
  1739. // Add items to the current line until it's full or we run out of items.
  1740. uint32_t endOfLineIndex = startOfLineIndex;
  1741. for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) {
  1742. const YGNodeRef child = node->getChild(endOfLineIndex);
  1743. if (child->getStyle().display == YGDisplayNone ||
  1744. child->getStyle().positionType == YGPositionTypeAbsolute) {
  1745. continue;
  1746. }
  1747. child->setLineIndex(lineCount);
  1748. const float childMarginMainAxis =
  1749. child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
  1750. const float flexBasisWithMinAndMaxConstraints =
  1751. YGNodeBoundAxisWithinMinAndMax(
  1752. child,
  1753. mainAxis,
  1754. child->getLayout().computedFlexBasis,
  1755. mainAxisownerSize)
  1756. .unwrap();
  1757. // If this is a multi-line flow and this item pushes us over the available
  1758. // size, we've hit the end of the current line. Break out of the loop and
  1759. // lay out the current line.
  1760. if (sizeConsumedOnCurrentLineIncludingMinConstraint +
  1761. flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
  1762. availableInnerMainDim &&
  1763. isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
  1764. break;
  1765. }
  1766. sizeConsumedOnCurrentLineIncludingMinConstraint +=
  1767. flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
  1768. flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
  1769. flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
  1770. flexAlgoRowMeasurement.itemsOnLine++;
  1771. if (child->isNodeFlexible()) {
  1772. flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow();
  1773. // Unlike the grow factor, the shrink factor is scaled relative to the
  1774. // child dimension.
  1775. flexAlgoRowMeasurement.totalFlexShrinkScaledFactors +=
  1776. -child->resolveFlexShrink() *
  1777. child->getLayout().computedFlexBasis.unwrap();
  1778. }
  1779. flexAlgoRowMeasurement.relativeChildren.push_back(child);
  1780. }
  1781. // The total flex factor needs to be floored to 1.
  1782. if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 &&
  1783. flexAlgoRowMeasurement.totalFlexGrowFactors < 1) {
  1784. flexAlgoRowMeasurement.totalFlexGrowFactors = 1;
  1785. }
  1786. // The total flex shrink factor needs to be floored to 1.
  1787. if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 &&
  1788. flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) {
  1789. flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1;
  1790. }
  1791. flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex;
  1792. return flexAlgoRowMeasurement;
  1793. }
  1794. // It distributes the free space to the flexible items and ensures that the size
  1795. // of the flex items abide the min and max constraints. At the end of this
  1796. // function the child nodes would have proper size. Prior using this function
  1797. // please ensure that YGDistributeFreeSpaceFirstPass is called.
  1798. static float YGDistributeFreeSpaceSecondPass(
  1799. YGCollectFlexItemsRowValues& collectedFlexItemsValues,
  1800. const YGNodeRef node,
  1801. const YGFlexDirection mainAxis,
  1802. const YGFlexDirection crossAxis,
  1803. const float mainAxisownerSize,
  1804. const float availableInnerMainDim,
  1805. const float availableInnerCrossDim,
  1806. const float availableInnerWidth,
  1807. const float availableInnerHeight,
  1808. const bool flexBasisOverflows,
  1809. const YGMeasureMode measureModeCrossDim,
  1810. const bool performLayout,
  1811. const YGConfigRef config,
  1812. YGMarkerLayoutData& layoutMarkerData,
  1813. void* const layoutContext) {
  1814. float childFlexBasis = 0;
  1815. float flexShrinkScaledFactor = 0;
  1816. float flexGrowFactor = 0;
  1817. float deltaFreeSpace = 0;
  1818. const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
  1819. const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
  1820. for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
  1821. childFlexBasis = YGNodeBoundAxisWithinMinAndMax(
  1822. currentRelativeChild,
  1823. mainAxis,
  1824. currentRelativeChild->getLayout().computedFlexBasis,
  1825. mainAxisownerSize)
  1826. .unwrap();
  1827. float updatedMainSize = childFlexBasis;
  1828. if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
  1829. collectedFlexItemsValues.remainingFreeSpace < 0) {
  1830. flexShrinkScaledFactor =
  1831. -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
  1832. // Is this child able to shrink?
  1833. if (flexShrinkScaledFactor != 0) {
  1834. float childSize;
  1835. if (!YGFloatIsUndefined(
  1836. collectedFlexItemsValues.totalFlexShrinkScaledFactors) &&
  1837. collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
  1838. childSize = childFlexBasis + flexShrinkScaledFactor;
  1839. } else {
  1840. childSize = childFlexBasis +
  1841. (collectedFlexItemsValues.remainingFreeSpace /
  1842. collectedFlexItemsValues.totalFlexShrinkScaledFactors) *
  1843. flexShrinkScaledFactor;
  1844. }
  1845. updatedMainSize = YGNodeBoundAxis(
  1846. currentRelativeChild,
  1847. mainAxis,
  1848. childSize,
  1849. availableInnerMainDim,
  1850. availableInnerWidth);
  1851. }
  1852. } else if (
  1853. !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
  1854. collectedFlexItemsValues.remainingFreeSpace > 0) {
  1855. flexGrowFactor = currentRelativeChild->resolveFlexGrow();
  1856. // Is this child able to grow?
  1857. if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
  1858. updatedMainSize = YGNodeBoundAxis(
  1859. currentRelativeChild,
  1860. mainAxis,
  1861. childFlexBasis +
  1862. collectedFlexItemsValues.remainingFreeSpace /
  1863. collectedFlexItemsValues.totalFlexGrowFactors *
  1864. flexGrowFactor,
  1865. availableInnerMainDim,
  1866. availableInnerWidth);
  1867. }
  1868. }
  1869. deltaFreeSpace += updatedMainSize - childFlexBasis;
  1870. const float marginMain =
  1871. currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth)
  1872. .unwrap();
  1873. const float marginCross =
  1874. currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth)
  1875. .unwrap();
  1876. float childCrossSize;
  1877. float childMainSize = updatedMainSize + marginMain;
  1878. YGMeasureMode childCrossMeasureMode;
  1879. YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
  1880. if (!currentRelativeChild->getStyle().aspectRatio.isUndefined()) {
  1881. childCrossSize = isMainAxisRow ? (childMainSize - marginMain) /
  1882. currentRelativeChild->getStyle().aspectRatio.unwrap()
  1883. : (childMainSize - marginMain) *
  1884. currentRelativeChild->getStyle().aspectRatio.unwrap();
  1885. childCrossMeasureMode = YGMeasureModeExactly;
  1886. childCrossSize += marginCross;
  1887. } else if (
  1888. !YGFloatIsUndefined(availableInnerCrossDim) &&
  1889. !YGNodeIsStyleDimDefined(
  1890. currentRelativeChild, crossAxis, availableInnerCrossDim) &&
  1891. measureModeCrossDim == YGMeasureModeExactly &&
  1892. !(isNodeFlexWrap && flexBasisOverflows) &&
  1893. YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
  1894. currentRelativeChild->marginLeadingValue(crossAxis).unit !=
  1895. YGUnitAuto &&
  1896. currentRelativeChild->marginTrailingValue(crossAxis).unit !=
  1897. YGUnitAuto) {
  1898. childCrossSize = availableInnerCrossDim;
  1899. childCrossMeasureMode = YGMeasureModeExactly;
  1900. } else if (!YGNodeIsStyleDimDefined(
  1901. currentRelativeChild, crossAxis, availableInnerCrossDim)) {
  1902. childCrossSize = availableInnerCrossDim;
  1903. childCrossMeasureMode = YGFloatIsUndefined(childCrossSize)
  1904. ? YGMeasureModeUndefined
  1905. : YGMeasureModeAtMost;
  1906. } else {
  1907. childCrossSize =
  1908. YGResolveValue(
  1909. currentRelativeChild->getResolvedDimension(dim[crossAxis]),
  1910. availableInnerCrossDim)
  1911. .unwrap() +
  1912. marginCross;
  1913. const bool isLoosePercentageMeasurement =
  1914. currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit ==
  1915. YGUnitPercent &&
  1916. measureModeCrossDim != YGMeasureModeExactly;
  1917. childCrossMeasureMode =
  1918. YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement
  1919. ? YGMeasureModeUndefined
  1920. : YGMeasureModeExactly;
  1921. }
  1922. YGConstrainMaxSizeForMode(
  1923. currentRelativeChild,
  1924. mainAxis,
  1925. availableInnerMainDim,
  1926. availableInnerWidth,
  1927. &childMainMeasureMode,
  1928. &childMainSize);
  1929. YGConstrainMaxSizeForMode(
  1930. currentRelativeChild,
  1931. crossAxis,
  1932. availableInnerCrossDim,
  1933. availableInnerWidth,
  1934. &childCrossMeasureMode,
  1935. &childCrossSize);
  1936. const bool requiresStretchLayout =
  1937. !YGNodeIsStyleDimDefined(
  1938. currentRelativeChild, crossAxis, availableInnerCrossDim) &&
  1939. YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
  1940. currentRelativeChild->marginLeadingValue(crossAxis).unit !=
  1941. YGUnitAuto &&
  1942. currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
  1943. const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
  1944. const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
  1945. const YGMeasureMode childWidthMeasureMode =
  1946. isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
  1947. const YGMeasureMode childHeightMeasureMode =
  1948. !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
  1949. // Recursively call the layout algorithm for this child with the updated
  1950. // main size.
  1951. YGLayoutNodeInternal(
  1952. currentRelativeChild,
  1953. childWidth,
  1954. childHeight,
  1955. node->getLayout().direction,
  1956. childWidthMeasureMode,
  1957. childHeightMeasureMode,
  1958. availableInnerWidth,
  1959. availableInnerHeight,
  1960. performLayout && !requiresStretchLayout,
  1961. "flex",
  1962. config,
  1963. layoutMarkerData,
  1964. layoutContext);
  1965. node->setLayoutHadOverflow(
  1966. node->getLayout().hadOverflow |
  1967. currentRelativeChild->getLayout().hadOverflow);
  1968. }
  1969. return deltaFreeSpace;
  1970. }
  1971. // It distributes the free space to the flexible items.For those flexible items
  1972. // whose min and max constraints are triggered, those flex item's clamped size
  1973. // is removed from the remaingfreespace.
  1974. static void YGDistributeFreeSpaceFirstPass(
  1975. YGCollectFlexItemsRowValues& collectedFlexItemsValues,
  1976. const YGFlexDirection mainAxis,
  1977. const float mainAxisownerSize,
  1978. const float availableInnerMainDim,
  1979. const float availableInnerWidth) {
  1980. float flexShrinkScaledFactor = 0;
  1981. float flexGrowFactor = 0;
  1982. float baseMainSize = 0;
  1983. float boundMainSize = 0;
  1984. float deltaFreeSpace = 0;
  1985. for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
  1986. float childFlexBasis =
  1987. YGNodeBoundAxisWithinMinAndMax(
  1988. currentRelativeChild,
  1989. mainAxis,
  1990. currentRelativeChild->getLayout().computedFlexBasis,
  1991. mainAxisownerSize)
  1992. .unwrap();
  1993. if (collectedFlexItemsValues.remainingFreeSpace < 0) {
  1994. flexShrinkScaledFactor =
  1995. -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
  1996. // Is this child able to shrink?
  1997. if (!YGFloatIsUndefined(flexShrinkScaledFactor) &&
  1998. flexShrinkScaledFactor != 0) {
  1999. baseMainSize = childFlexBasis +
  2000. collectedFlexItemsValues.remainingFreeSpace /
  2001. collectedFlexItemsValues.totalFlexShrinkScaledFactors *
  2002. flexShrinkScaledFactor;
  2003. boundMainSize = YGNodeBoundAxis(
  2004. currentRelativeChild,
  2005. mainAxis,
  2006. baseMainSize,
  2007. availableInnerMainDim,
  2008. availableInnerWidth);
  2009. if (!YGFloatIsUndefined(baseMainSize) &&
  2010. !YGFloatIsUndefined(boundMainSize) &&
  2011. baseMainSize != boundMainSize) {
  2012. // By excluding this item's size and flex factor from remaining, this
  2013. // item's min/max constraints should also trigger in the second pass
  2014. // resulting in the item's size calculation being identical in the
  2015. // first and second passes.
  2016. deltaFreeSpace += boundMainSize - childFlexBasis;
  2017. collectedFlexItemsValues.totalFlexShrinkScaledFactors -=
  2018. flexShrinkScaledFactor;
  2019. }
  2020. }
  2021. } else if (
  2022. !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
  2023. collectedFlexItemsValues.remainingFreeSpace > 0) {
  2024. flexGrowFactor = currentRelativeChild->resolveFlexGrow();
  2025. // Is this child able to grow?
  2026. if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
  2027. baseMainSize = childFlexBasis +
  2028. collectedFlexItemsValues.remainingFreeSpace /
  2029. collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor;
  2030. boundMainSize = YGNodeBoundAxis(
  2031. currentRelativeChild,
  2032. mainAxis,
  2033. baseMainSize,
  2034. availableInnerMainDim,
  2035. availableInnerWidth);
  2036. if (!YGFloatIsUndefined(baseMainSize) &&
  2037. !YGFloatIsUndefined(boundMainSize) &&
  2038. baseMainSize != boundMainSize) {
  2039. // By excluding this item's size and flex factor from remaining, this
  2040. // item's min/max constraints should also trigger in the second pass
  2041. // resulting in the item's size calculation being identical in the
  2042. // first and second passes.
  2043. deltaFreeSpace += boundMainSize - childFlexBasis;
  2044. collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor;
  2045. }
  2046. }
  2047. }
  2048. }
  2049. collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace;
  2050. }
  2051. // Do two passes over the flex items to figure out how to distribute the
  2052. // remaining space.
  2053. //
  2054. // The first pass finds the items whose min/max constraints trigger, freezes
  2055. // them at those sizes, and excludes those sizes from the remaining space.
  2056. //
  2057. // The second pass sets the size of each flexible item. It distributes the
  2058. // remaining space amongst the items whose min/max constraints didn't trigger in
  2059. // the first pass. For the other items, it sets their sizes by forcing their
  2060. // min/max constraints to trigger again.
  2061. //
  2062. // This two pass approach for resolving min/max constraints deviates from the
  2063. // spec. The spec
  2064. // (https://www.w3.org/TR/CSS-flexbox-1/#resolve-flexible-lengths) describes a
  2065. // process that needs to be repeated a variable number of times. The algorithm
  2066. // implemented here won't handle all cases but it was simpler to implement and
  2067. // it mitigates performance concerns because we know exactly how many passes
  2068. // it'll do.
  2069. //
  2070. // At the end of this function the child nodes would have the proper size
  2071. // assigned to them.
  2072. //
  2073. static void YGResolveFlexibleLength(
  2074. const YGNodeRef node,
  2075. YGCollectFlexItemsRowValues& collectedFlexItemsValues,
  2076. const YGFlexDirection mainAxis,
  2077. const YGFlexDirection crossAxis,
  2078. const float mainAxisownerSize,
  2079. const float availableInnerMainDim,
  2080. const float availableInnerCrossDim,
  2081. const float availableInnerWidth,
  2082. const float availableInnerHeight,
  2083. const bool flexBasisOverflows,
  2084. const YGMeasureMode measureModeCrossDim,
  2085. const bool performLayout,
  2086. const YGConfigRef config,
  2087. YGMarkerLayoutData& layoutMarkerData,
  2088. void* const layoutContext) {
  2089. const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace;
  2090. // First pass: detect the flex items whose min/max constraints trigger
  2091. YGDistributeFreeSpaceFirstPass(
  2092. collectedFlexItemsValues,
  2093. mainAxis,
  2094. mainAxisownerSize,
  2095. availableInnerMainDim,
  2096. availableInnerWidth);
  2097. // Second pass: resolve the sizes of the flexible items
  2098. const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass(
  2099. collectedFlexItemsValues,
  2100. node,
  2101. mainAxis,
  2102. crossAxis,
  2103. mainAxisownerSize,
  2104. availableInnerMainDim,
  2105. availableInnerCrossDim,
  2106. availableInnerWidth,
  2107. availableInnerHeight,
  2108. flexBasisOverflows,
  2109. measureModeCrossDim,
  2110. performLayout,
  2111. config,
  2112. layoutMarkerData,
  2113. layoutContext);
  2114. collectedFlexItemsValues.remainingFreeSpace =
  2115. originalFreeSpace - distributedFreeSpace;
  2116. }
  2117. static void YGJustifyMainAxis(
  2118. const YGNodeRef node,
  2119. YGCollectFlexItemsRowValues& collectedFlexItemsValues,
  2120. const uint32_t startOfLineIndex,
  2121. const YGFlexDirection mainAxis,
  2122. const YGFlexDirection crossAxis,
  2123. const YGMeasureMode measureModeMainDim,
  2124. const YGMeasureMode measureModeCrossDim,
  2125. const float mainAxisownerSize,
  2126. const float ownerWidth,
  2127. const float availableInnerMainDim,
  2128. const float availableInnerCrossDim,
  2129. const float availableInnerWidth,
  2130. const bool performLayout,
  2131. void* const layoutContext) {
  2132. const YGStyle& style = node->getStyle();
  2133. const float leadingPaddingAndBorderMain =
  2134. node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
  2135. const float trailingPaddingAndBorderMain =
  2136. node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
  2137. // If we are using "at most" rules in the main axis, make sure that
  2138. // remainingFreeSpace is 0 when min main dimension is not given
  2139. if (measureModeMainDim == YGMeasureModeAtMost &&
  2140. collectedFlexItemsValues.remainingFreeSpace > 0) {
  2141. if (!style.minDimensions[dim[mainAxis]].isUndefined() &&
  2142. !YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize)
  2143. .isUndefined()) {
  2144. // This condition makes sure that if the size of main dimension(after
  2145. // considering child nodes main dim, leading and trailing padding etc)
  2146. // falls below min dimension, then the remainingFreeSpace is reassigned
  2147. // considering the min dimension
  2148. // `minAvailableMainDim` denotes minimum available space in which child
  2149. // can be laid out, it will exclude space consumed by padding and border.
  2150. const float minAvailableMainDim =
  2151. YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize)
  2152. .unwrap() -
  2153. leadingPaddingAndBorderMain - trailingPaddingAndBorderMain;
  2154. const float occupiedSpaceByChildNodes =
  2155. availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace;
  2156. collectedFlexItemsValues.remainingFreeSpace =
  2157. YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes);
  2158. } else {
  2159. collectedFlexItemsValues.remainingFreeSpace = 0;
  2160. }
  2161. }
  2162. int numberOfAutoMarginsOnCurrentLine = 0;
  2163. for (uint32_t i = startOfLineIndex;
  2164. i < collectedFlexItemsValues.endOfLineIndex;
  2165. i++) {
  2166. const YGNodeRef child = node->getChild(i);
  2167. if (child->getStyle().positionType == YGPositionTypeRelative) {
  2168. if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
  2169. numberOfAutoMarginsOnCurrentLine++;
  2170. }
  2171. if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
  2172. numberOfAutoMarginsOnCurrentLine++;
  2173. }
  2174. }
  2175. }
  2176. // In order to position the elements in the main axis, we have two controls.
  2177. // The space between the beginning and the first element and the space between
  2178. // each two elements.
  2179. float leadingMainDim = 0;
  2180. float betweenMainDim = 0;
  2181. const YGJustify justifyContent = node->getStyle().justifyContent;
  2182. if (numberOfAutoMarginsOnCurrentLine == 0) {
  2183. switch (justifyContent) {
  2184. case YGJustifyCenter:
  2185. leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2;
  2186. break;
  2187. case YGJustifyFlexEnd:
  2188. leadingMainDim = collectedFlexItemsValues.remainingFreeSpace;
  2189. break;
  2190. case YGJustifySpaceBetween:
  2191. if (collectedFlexItemsValues.itemsOnLine > 1) {
  2192. betweenMainDim =
  2193. YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
  2194. (collectedFlexItemsValues.itemsOnLine - 1);
  2195. } else {
  2196. betweenMainDim = 0;
  2197. }
  2198. break;
  2199. case YGJustifySpaceEvenly:
  2200. // Space is distributed evenly across all elements
  2201. betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
  2202. (collectedFlexItemsValues.itemsOnLine + 1);
  2203. leadingMainDim = betweenMainDim;
  2204. break;
  2205. case YGJustifySpaceAround:
  2206. // Space on the edges is half of the space between elements
  2207. betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
  2208. collectedFlexItemsValues.itemsOnLine;
  2209. leadingMainDim = betweenMainDim / 2;
  2210. break;
  2211. case YGJustifyFlexStart:
  2212. break;
  2213. }
  2214. }
  2215. collectedFlexItemsValues.mainDim =
  2216. leadingPaddingAndBorderMain + leadingMainDim;
  2217. collectedFlexItemsValues.crossDim = 0;
  2218. float maxAscentForCurrentLine = 0;
  2219. float maxDescentForCurrentLine = 0;
  2220. bool isNodeBaselineLayout = YGIsBaselineLayout(node);
  2221. for (uint32_t i = startOfLineIndex;
  2222. i < collectedFlexItemsValues.endOfLineIndex;
  2223. i++) {
  2224. const YGNodeRef child = node->getChild(i);
  2225. const YGStyle& childStyle = child->getStyle();
  2226. const YGLayout childLayout = child->getLayout();
  2227. if (childStyle.display == YGDisplayNone) {
  2228. continue;
  2229. }
  2230. if (childStyle.positionType == YGPositionTypeAbsolute &&
  2231. child->isLeadingPositionDefined(mainAxis)) {
  2232. if (performLayout) {
  2233. // In case the child is position absolute and has left/top being
  2234. // defined, we override the position to whatever the user said (and
  2235. // margin/border).
  2236. child->setLayoutPosition(
  2237. child->getLeadingPosition(mainAxis, availableInnerMainDim)
  2238. .unwrap() +
  2239. node->getLeadingBorder(mainAxis) +
  2240. child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(),
  2241. pos[mainAxis]);
  2242. }
  2243. } else {
  2244. // Now that we placed the element, we need to update the variables.
  2245. // We need to do that only for relative elements. Absolute elements do not
  2246. // take part in that phase.
  2247. if (childStyle.positionType == YGPositionTypeRelative) {
  2248. if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
  2249. collectedFlexItemsValues.mainDim +=
  2250. collectedFlexItemsValues.remainingFreeSpace /
  2251. numberOfAutoMarginsOnCurrentLine;
  2252. }
  2253. if (performLayout) {
  2254. child->setLayoutPosition(
  2255. childLayout.position[pos[mainAxis]] +
  2256. collectedFlexItemsValues.mainDim,
  2257. pos[mainAxis]);
  2258. }
  2259. if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
  2260. collectedFlexItemsValues.mainDim +=
  2261. collectedFlexItemsValues.remainingFreeSpace /
  2262. numberOfAutoMarginsOnCurrentLine;
  2263. }
  2264. bool canSkipFlex =
  2265. !performLayout && measureModeCrossDim == YGMeasureModeExactly;
  2266. if (canSkipFlex) {
  2267. // If we skipped the flex step, then we can't rely on the measuredDims
  2268. // because they weren't computed. This means we can't call
  2269. // YGNodeDimWithMargin.
  2270. collectedFlexItemsValues.mainDim += betweenMainDim +
  2271. child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() +
  2272. childLayout.computedFlexBasis.unwrap();
  2273. collectedFlexItemsValues.crossDim = availableInnerCrossDim;
  2274. } else {
  2275. // The main dimension is the sum of all the elements dimension plus
  2276. // the spacing.
  2277. collectedFlexItemsValues.mainDim += betweenMainDim +
  2278. YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);
  2279. if (isNodeBaselineLayout) {
  2280. // If the child is baseline aligned then the cross dimension is
  2281. // calculated by adding maxAscent and maxDescent from the baseline.
  2282. const float ascent = YGBaseline(child, layoutContext) +
  2283. child
  2284. ->getLeadingMargin(
  2285. YGFlexDirectionColumn, availableInnerWidth)
  2286. .unwrap();
  2287. const float descent =
  2288. child->getLayout().measuredDimensions[YGDimensionHeight] +
  2289. child
  2290. ->getMarginForAxis(
  2291. YGFlexDirectionColumn, availableInnerWidth)
  2292. .unwrap() -
  2293. ascent;
  2294. maxAscentForCurrentLine =
  2295. YGFloatMax(maxAscentForCurrentLine, ascent);
  2296. maxDescentForCurrentLine =
  2297. YGFloatMax(maxDescentForCurrentLine, descent);
  2298. } else {
  2299. // The cross dimension is the max of the elements dimension since
  2300. // there can only be one element in that cross dimension in the case
  2301. // when the items are not baseline aligned
  2302. collectedFlexItemsValues.crossDim = YGFloatMax(
  2303. collectedFlexItemsValues.crossDim,
  2304. YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));
  2305. }
  2306. }
  2307. } else if (performLayout) {
  2308. child->setLayoutPosition(
  2309. childLayout.position[pos[mainAxis]] +
  2310. node->getLeadingBorder(mainAxis) + leadingMainDim,
  2311. pos[mainAxis]);
  2312. }
  2313. }
  2314. }
  2315. collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain;
  2316. if (isNodeBaselineLayout) {
  2317. collectedFlexItemsValues.crossDim =
  2318. maxAscentForCurrentLine + maxDescentForCurrentLine;
  2319. }
  2320. }
  2321. //
  2322. // This is the main routine that implements a subset of the flexbox layout
  2323. // algorithm described in the W3C CSS documentation:
  2324. // https://www.w3.org/TR/CSS3-flexbox/.
  2325. //
  2326. // Limitations of this algorithm, compared to the full standard:
  2327. // * Display property is always assumed to be 'flex' except for Text nodes,
  2328. // which are assumed to be 'inline-flex'.
  2329. // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes
  2330. // are stacked in document order.
  2331. // * The 'order' property is not supported. The order of flex items is always
  2332. // defined by document order.
  2333. // * The 'visibility' property is always assumed to be 'visible'. Values of
  2334. // 'collapse' and 'hidden' are not supported.
  2335. // * There is no support for forced breaks.
  2336. // * It does not support vertical inline directions (top-to-bottom or
  2337. // bottom-to-top text).
  2338. //
  2339. // Deviations from standard:
  2340. // * Section 4.5 of the spec indicates that all flex items have a default
  2341. // minimum main size. For text blocks, for example, this is the width of the
  2342. // widest word. Calculating the minimum width is expensive, so we forego it
  2343. // and assume a default minimum main size of 0.
  2344. // * Min/Max sizes in the main axis are not honored when resolving flexible
  2345. // lengths.
  2346. // * The spec indicates that the default value for 'flexDirection' is 'row',
  2347. // but the algorithm below assumes a default of 'column'.
  2348. //
  2349. // Input parameters:
  2350. // - node: current node to be sized and layed out
  2351. // - availableWidth & availableHeight: available size to be used for sizing
  2352. // the node or YGUndefined if the size is not available; interpretation
  2353. // depends on layout flags
  2354. // - ownerDirection: the inline (text) direction within the owner
  2355. // (left-to-right or right-to-left)
  2356. // - widthMeasureMode: indicates the sizing rules for the width (see below
  2357. // for explanation)
  2358. // - heightMeasureMode: indicates the sizing rules for the height (see below
  2359. // for explanation)
  2360. // - performLayout: specifies whether the caller is interested in just the
  2361. // dimensions of the node or it requires the entire node and its subtree to
  2362. // be layed out (with final positions)
  2363. //
  2364. // Details:
  2365. // This routine is called recursively to lay out subtrees of flexbox
  2366. // elements. It uses the information in node.style, which is treated as a
  2367. // read-only input. It is responsible for setting the layout.direction and
  2368. // layout.measuredDimensions fields for the input node as well as the
  2369. // layout.position and layout.lineIndex fields for its child nodes. The
  2370. // layout.measuredDimensions field includes any border or padding for the
  2371. // node but does not include margins.
  2372. //
  2373. // The spec describes four different layout modes: "fill available", "max
  2374. // content", "min content", and "fit content". Of these, we don't use "min
  2375. // content" because we don't support default minimum main sizes (see above
  2376. // for details). Each of our measure modes maps to a layout mode from the
  2377. // spec (https://www.w3.org/TR/CSS3-sizing/#terms):
  2378. // - YGMeasureModeUndefined: max content
  2379. // - YGMeasureModeExactly: fill available
  2380. // - YGMeasureModeAtMost: fit content
  2381. //
  2382. // When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller
  2383. // passes an available size of undefined then it must also pass a measure
  2384. // mode of YGMeasureModeUndefined in that dimension.
  2385. //
  2386. static void YGNodelayoutImpl(
  2387. const YGNodeRef node,
  2388. const float availableWidth,
  2389. const float availableHeight,
  2390. const YGDirection ownerDirection,
  2391. const YGMeasureMode widthMeasureMode,
  2392. const YGMeasureMode heightMeasureMode,
  2393. const float ownerWidth,
  2394. const float ownerHeight,
  2395. const bool performLayout,
  2396. const YGConfigRef config,
  2397. YGMarkerLayoutData& layoutMarkerData,
  2398. void* const layoutContext) {
  2399. YGAssertWithNode(
  2400. node,
  2401. YGFloatIsUndefined(availableWidth)
  2402. ? widthMeasureMode == YGMeasureModeUndefined
  2403. : true,
  2404. "availableWidth is indefinite so widthMeasureMode must be "
  2405. "YGMeasureModeUndefined");
  2406. YGAssertWithNode(
  2407. node,
  2408. YGFloatIsUndefined(availableHeight)
  2409. ? heightMeasureMode == YGMeasureModeUndefined
  2410. : true,
  2411. "availableHeight is indefinite so heightMeasureMode must be "
  2412. "YGMeasureModeUndefined");
  2413. (performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1;
  2414. // Set the resolved resolution in the node's layout.
  2415. const YGDirection direction = node->resolveDirection(ownerDirection);
  2416. node->setLayoutDirection(direction);
  2417. const YGFlexDirection flexRowDirection =
  2418. YGResolveFlexDirection(YGFlexDirectionRow, direction);
  2419. const YGFlexDirection flexColumnDirection =
  2420. YGResolveFlexDirection(YGFlexDirectionColumn, direction);
  2421. node->setLayoutMargin(
  2422. node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap(),
  2423. YGEdgeStart);
  2424. node->setLayoutMargin(
  2425. node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap(),
  2426. YGEdgeEnd);
  2427. node->setLayoutMargin(
  2428. node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap(),
  2429. YGEdgeTop);
  2430. node->setLayoutMargin(
  2431. node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap(),
  2432. YGEdgeBottom);
  2433. node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), YGEdgeStart);
  2434. node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), YGEdgeEnd);
  2435. node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop);
  2436. node->setLayoutBorder(
  2437. node->getTrailingBorder(flexColumnDirection), YGEdgeBottom);
  2438. node->setLayoutPadding(
  2439. node->getLeadingPadding(flexRowDirection, ownerWidth).unwrap(),
  2440. YGEdgeStart);
  2441. node->setLayoutPadding(
  2442. node->getTrailingPadding(flexRowDirection, ownerWidth).unwrap(),
  2443. YGEdgeEnd);
  2444. node->setLayoutPadding(
  2445. node->getLeadingPadding(flexColumnDirection, ownerWidth).unwrap(),
  2446. YGEdgeTop);
  2447. node->setLayoutPadding(
  2448. node->getTrailingPadding(flexColumnDirection, ownerWidth).unwrap(),
  2449. YGEdgeBottom);
  2450. if (node->hasMeasureFunc()) {
  2451. YGNodeWithMeasureFuncSetMeasuredDimensions(
  2452. node,
  2453. availableWidth,
  2454. availableHeight,
  2455. widthMeasureMode,
  2456. heightMeasureMode,
  2457. ownerWidth,
  2458. ownerHeight,
  2459. layoutContext);
  2460. return;
  2461. }
  2462. const uint32_t childCount = YGNodeGetChildCount(node);
  2463. if (childCount == 0) {
  2464. YGNodeEmptyContainerSetMeasuredDimensions(
  2465. node,
  2466. availableWidth,
  2467. availableHeight,
  2468. widthMeasureMode,
  2469. heightMeasureMode,
  2470. ownerWidth,
  2471. ownerHeight);
  2472. return;
  2473. }
  2474. // If we're not being asked to perform a full layout we can skip the algorithm
  2475. // if we already know the size
  2476. if (!performLayout &&
  2477. YGNodeFixedSizeSetMeasuredDimensions(
  2478. node,
  2479. availableWidth,
  2480. availableHeight,
  2481. widthMeasureMode,
  2482. heightMeasureMode,
  2483. ownerWidth,
  2484. ownerHeight)) {
  2485. return;
  2486. }
  2487. // At this point we know we're going to perform work. Ensure that each child
  2488. // has a mutable copy.
  2489. node->cloneChildrenIfNeeded(layoutContext);
  2490. // Reset layout flags, as they could have changed.
  2491. node->setLayoutHadOverflow(false);
  2492. // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
  2493. const YGFlexDirection mainAxis =
  2494. YGResolveFlexDirection(node->getStyle().flexDirection, direction);
  2495. const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
  2496. const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
  2497. const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
  2498. const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
  2499. const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth;
  2500. const float leadingPaddingAndBorderCross =
  2501. node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap();
  2502. const float paddingAndBorderAxisMain =
  2503. YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth);
  2504. const float paddingAndBorderAxisCross =
  2505. YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth);
  2506. YGMeasureMode measureModeMainDim =
  2507. isMainAxisRow ? widthMeasureMode : heightMeasureMode;
  2508. YGMeasureMode measureModeCrossDim =
  2509. isMainAxisRow ? heightMeasureMode : widthMeasureMode;
  2510. const float paddingAndBorderAxisRow =
  2511. isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;
  2512. const float paddingAndBorderAxisColumn =
  2513. isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;
  2514. const float marginAxisRow =
  2515. node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
  2516. const float marginAxisColumn =
  2517. node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
  2518. const float minInnerWidth =
  2519. YGResolveValue(
  2520. node->getStyle().minDimensions[YGDimensionWidth], ownerWidth)
  2521. .unwrap() -
  2522. paddingAndBorderAxisRow;
  2523. const float maxInnerWidth =
  2524. YGResolveValue(
  2525. node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)
  2526. .unwrap() -
  2527. paddingAndBorderAxisRow;
  2528. const float minInnerHeight =
  2529. YGResolveValue(
  2530. node->getStyle().minDimensions[YGDimensionHeight], ownerHeight)
  2531. .unwrap() -
  2532. paddingAndBorderAxisColumn;
  2533. const float maxInnerHeight =
  2534. YGResolveValue(
  2535. node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight)
  2536. .unwrap() -
  2537. paddingAndBorderAxisColumn;
  2538. const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight;
  2539. const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight;
  2540. // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
  2541. float availableInnerWidth = YGNodeCalculateAvailableInnerDim(
  2542. node, YGFlexDirectionRow, availableWidth, ownerWidth);
  2543. float availableInnerHeight = YGNodeCalculateAvailableInnerDim(
  2544. node, YGFlexDirectionColumn, availableHeight, ownerHeight);
  2545. float availableInnerMainDim =
  2546. isMainAxisRow ? availableInnerWidth : availableInnerHeight;
  2547. const float availableInnerCrossDim =
  2548. isMainAxisRow ? availableInnerHeight : availableInnerWidth;
  2549. // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
  2550. float totalOuterFlexBasis = YGNodeComputeFlexBasisForChildren(
  2551. node,
  2552. availableInnerWidth,
  2553. availableInnerHeight,
  2554. widthMeasureMode,
  2555. heightMeasureMode,
  2556. direction,
  2557. mainAxis,
  2558. config,
  2559. performLayout,
  2560. layoutMarkerData,
  2561. layoutContext);
  2562. const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined
  2563. ? false
  2564. : totalOuterFlexBasis > availableInnerMainDim;
  2565. if (isNodeFlexWrap && flexBasisOverflows &&
  2566. measureModeMainDim == YGMeasureModeAtMost) {
  2567. measureModeMainDim = YGMeasureModeExactly;
  2568. }
  2569. // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
  2570. // Indexes of children that represent the first and last items in the line.
  2571. uint32_t startOfLineIndex = 0;
  2572. uint32_t endOfLineIndex = 0;
  2573. // Number of lines.
  2574. uint32_t lineCount = 0;
  2575. // Accumulated cross dimensions of all lines so far.
  2576. float totalLineCrossDim = 0;
  2577. // Max main dimension of all the lines.
  2578. float maxLineMainDim = 0;
  2579. YGCollectFlexItemsRowValues collectedFlexItemsValues;
  2580. for (; endOfLineIndex < childCount;
  2581. lineCount++, startOfLineIndex = endOfLineIndex) {
  2582. collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues(
  2583. node,
  2584. ownerDirection,
  2585. mainAxisownerSize,
  2586. availableInnerWidth,
  2587. availableInnerMainDim,
  2588. startOfLineIndex,
  2589. lineCount);
  2590. endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
  2591. // If we don't need to measure the cross axis, we can skip the entire flex
  2592. // step.
  2593. const bool canSkipFlex =
  2594. !performLayout && measureModeCrossDim == YGMeasureModeExactly;
  2595. // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
  2596. // Calculate the remaining available space that needs to be allocated. If
  2597. // the main dimension size isn't known, it is computed based on the line
  2598. // length, so there's no more space left to distribute.
  2599. bool sizeBasedOnContent = false;
  2600. // If we don't measure with exact main dimension we want to ensure we don't
  2601. // violate min and max
  2602. if (measureModeMainDim != YGMeasureModeExactly) {
  2603. if (!YGFloatIsUndefined(minInnerMainDim) &&
  2604. collectedFlexItemsValues.sizeConsumedOnCurrentLine <
  2605. minInnerMainDim) {
  2606. availableInnerMainDim = minInnerMainDim;
  2607. } else if (
  2608. !YGFloatIsUndefined(maxInnerMainDim) &&
  2609. collectedFlexItemsValues.sizeConsumedOnCurrentLine >
  2610. maxInnerMainDim) {
  2611. availableInnerMainDim = maxInnerMainDim;
  2612. } else {
  2613. if (!node->getConfig()->useLegacyStretchBehaviour &&
  2614. ((YGFloatIsUndefined(
  2615. collectedFlexItemsValues.totalFlexGrowFactors) &&
  2616. collectedFlexItemsValues.totalFlexGrowFactors == 0) ||
  2617. (YGFloatIsUndefined(node->resolveFlexGrow()) &&
  2618. node->resolveFlexGrow() == 0))) {
  2619. // If we don't have any children to flex or we can't flex the node
  2620. // itself, space we've used is all space we need. Root node also
  2621. // should be shrunk to minimum
  2622. availableInnerMainDim =
  2623. collectedFlexItemsValues.sizeConsumedOnCurrentLine;
  2624. }
  2625. if (node->getConfig()->useLegacyStretchBehaviour) {
  2626. node->setLayoutDidUseLegacyFlag(true);
  2627. }
  2628. sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour;
  2629. }
  2630. }
  2631. if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) {
  2632. collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim -
  2633. collectedFlexItemsValues.sizeConsumedOnCurrentLine;
  2634. } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) {
  2635. // availableInnerMainDim is indefinite which means the node is being sized
  2636. // based on its content. sizeConsumedOnCurrentLine is negative which means
  2637. // the node will allocate 0 points for its content. Consequently,
  2638. // remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
  2639. collectedFlexItemsValues.remainingFreeSpace =
  2640. -collectedFlexItemsValues.sizeConsumedOnCurrentLine;
  2641. }
  2642. if (!canSkipFlex) {
  2643. YGResolveFlexibleLength(
  2644. node,
  2645. collectedFlexItemsValues,
  2646. mainAxis,
  2647. crossAxis,
  2648. mainAxisownerSize,
  2649. availableInnerMainDim,
  2650. availableInnerCrossDim,
  2651. availableInnerWidth,
  2652. availableInnerHeight,
  2653. flexBasisOverflows,
  2654. measureModeCrossDim,
  2655. performLayout,
  2656. config,
  2657. layoutMarkerData,
  2658. layoutContext);
  2659. }
  2660. node->setLayoutHadOverflow(
  2661. node->getLayout().hadOverflow |
  2662. (collectedFlexItemsValues.remainingFreeSpace < 0));
  2663. // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
  2664. // At this point, all the children have their dimensions set in the main
  2665. // axis. Their dimensions are also set in the cross axis with the exception
  2666. // of items that are aligned "stretch". We need to compute these stretch
  2667. // values and set the final positions.
  2668. YGJustifyMainAxis(
  2669. node,
  2670. collectedFlexItemsValues,
  2671. startOfLineIndex,
  2672. mainAxis,
  2673. crossAxis,
  2674. measureModeMainDim,
  2675. measureModeCrossDim,
  2676. mainAxisownerSize,
  2677. ownerWidth,
  2678. availableInnerMainDim,
  2679. availableInnerCrossDim,
  2680. availableInnerWidth,
  2681. performLayout,
  2682. layoutContext);
  2683. float containerCrossAxis = availableInnerCrossDim;
  2684. if (measureModeCrossDim == YGMeasureModeUndefined ||
  2685. measureModeCrossDim == YGMeasureModeAtMost) {
  2686. // Compute the cross axis from the max cross dimension of the children.
  2687. containerCrossAxis =
  2688. YGNodeBoundAxis(
  2689. node,
  2690. crossAxis,
  2691. collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
  2692. crossAxisownerSize,
  2693. ownerWidth) -
  2694. paddingAndBorderAxisCross;
  2695. }
  2696. // If there's no flex wrap, the cross dimension is defined by the container.
  2697. if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {
  2698. collectedFlexItemsValues.crossDim = availableInnerCrossDim;
  2699. }
  2700. // Clamp to the min/max size specified on the container.
  2701. collectedFlexItemsValues.crossDim =
  2702. YGNodeBoundAxis(
  2703. node,
  2704. crossAxis,
  2705. collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
  2706. crossAxisownerSize,
  2707. ownerWidth) -
  2708. paddingAndBorderAxisCross;
  2709. // STEP 7: CROSS-AXIS ALIGNMENT
  2710. // We can skip child alignment if we're just measuring the container.
  2711. if (performLayout) {
  2712. for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {
  2713. const YGNodeRef child = node->getChild(i);
  2714. if (child->getStyle().display == YGDisplayNone) {
  2715. continue;
  2716. }
  2717. if (child->getStyle().positionType == YGPositionTypeAbsolute) {
  2718. // If the child is absolutely positioned and has a
  2719. // top/left/bottom/right set, override all the previously computed
  2720. // positions to set it correctly.
  2721. const bool isChildLeadingPosDefined =
  2722. child->isLeadingPositionDefined(crossAxis);
  2723. if (isChildLeadingPosDefined) {
  2724. child->setLayoutPosition(
  2725. child->getLeadingPosition(crossAxis, availableInnerCrossDim)
  2726. .unwrap() +
  2727. node->getLeadingBorder(crossAxis) +
  2728. child->getLeadingMargin(crossAxis, availableInnerWidth)
  2729. .unwrap(),
  2730. pos[crossAxis]);
  2731. }
  2732. // If leading position is not defined or calculations result in Nan,
  2733. // default to border + margin
  2734. if (!isChildLeadingPosDefined ||
  2735. YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) {
  2736. child->setLayoutPosition(
  2737. node->getLeadingBorder(crossAxis) +
  2738. child->getLeadingMargin(crossAxis, availableInnerWidth)
  2739. .unwrap(),
  2740. pos[crossAxis]);
  2741. }
  2742. } else {
  2743. float leadingCrossDim = leadingPaddingAndBorderCross;
  2744. // For a relative children, we're either using alignItems (owner) or
  2745. // alignSelf (child) in order to determine the position in the cross
  2746. // axis
  2747. const YGAlign alignItem = YGNodeAlignItem(node, child);
  2748. // If the child uses align stretch, we need to lay it out one more
  2749. // time, this time forcing the cross-axis size to be the computed
  2750. // cross size for the current line.
  2751. if (alignItem == YGAlignStretch &&
  2752. child->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
  2753. child->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
  2754. // If the child defines a definite size for its cross axis, there's
  2755. // no need to stretch.
  2756. if (!YGNodeIsStyleDimDefined(
  2757. child, crossAxis, availableInnerCrossDim)) {
  2758. float childMainSize =
  2759. child->getLayout().measuredDimensions[dim[mainAxis]];
  2760. float childCrossSize =
  2761. !child->getStyle().aspectRatio.isUndefined()
  2762. ? child->getMarginForAxis(crossAxis, availableInnerWidth)
  2763. .unwrap() +
  2764. (isMainAxisRow ? childMainSize /
  2765. child->getStyle().aspectRatio.unwrap()
  2766. : childMainSize *
  2767. child->getStyle().aspectRatio.unwrap())
  2768. : collectedFlexItemsValues.crossDim;
  2769. childMainSize +=
  2770. child->getMarginForAxis(mainAxis, availableInnerWidth)
  2771. .unwrap();
  2772. YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
  2773. YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly;
  2774. YGConstrainMaxSizeForMode(
  2775. child,
  2776. mainAxis,
  2777. availableInnerMainDim,
  2778. availableInnerWidth,
  2779. &childMainMeasureMode,
  2780. &childMainSize);
  2781. YGConstrainMaxSizeForMode(
  2782. child,
  2783. crossAxis,
  2784. availableInnerCrossDim,
  2785. availableInnerWidth,
  2786. &childCrossMeasureMode,
  2787. &childCrossSize);
  2788. const float childWidth =
  2789. isMainAxisRow ? childMainSize : childCrossSize;
  2790. const float childHeight =
  2791. !isMainAxisRow ? childMainSize : childCrossSize;
  2792. auto alignContent = node->getStyle().alignContent;
  2793. auto crossAxisDoesNotGrow =
  2794. alignContent != YGAlignStretch && isNodeFlexWrap;
  2795. const YGMeasureMode childWidthMeasureMode =
  2796. YGFloatIsUndefined(childWidth) ||
  2797. (!isMainAxisRow && crossAxisDoesNotGrow)
  2798. ? YGMeasureModeUndefined
  2799. : YGMeasureModeExactly;
  2800. const YGMeasureMode childHeightMeasureMode =
  2801. YGFloatIsUndefined(childHeight) ||
  2802. (isMainAxisRow && crossAxisDoesNotGrow)
  2803. ? YGMeasureModeUndefined
  2804. : YGMeasureModeExactly;
  2805. YGLayoutNodeInternal(
  2806. child,
  2807. childWidth,
  2808. childHeight,
  2809. direction,
  2810. childWidthMeasureMode,
  2811. childHeightMeasureMode,
  2812. availableInnerWidth,
  2813. availableInnerHeight,
  2814. true,
  2815. "stretch",
  2816. config,
  2817. layoutMarkerData,
  2818. layoutContext);
  2819. }
  2820. } else {
  2821. const float remainingCrossDim = containerCrossAxis -
  2822. YGNodeDimWithMargin(child, crossAxis, availableInnerWidth);
  2823. if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto &&
  2824. child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
  2825. leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2);
  2826. } else if (
  2827. child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
  2828. // No-Op
  2829. } else if (
  2830. child->marginLeadingValue(crossAxis).unit == YGUnitAuto) {
  2831. leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim);
  2832. } else if (alignItem == YGAlignFlexStart) {
  2833. // No-Op
  2834. } else if (alignItem == YGAlignCenter) {
  2835. leadingCrossDim += remainingCrossDim / 2;
  2836. } else {
  2837. leadingCrossDim += remainingCrossDim;
  2838. }
  2839. }
  2840. // And we apply the position
  2841. child->setLayoutPosition(
  2842. child->getLayout().position[pos[crossAxis]] + totalLineCrossDim +
  2843. leadingCrossDim,
  2844. pos[crossAxis]);
  2845. }
  2846. }
  2847. }
  2848. totalLineCrossDim += collectedFlexItemsValues.crossDim;
  2849. maxLineMainDim =
  2850. YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
  2851. }
  2852. // STEP 8: MULTI-LINE CONTENT ALIGNMENT
  2853. // currentLead stores the size of the cross dim
  2854. if (performLayout && (isNodeFlexWrap || YGIsBaselineLayout(node))) {
  2855. float crossDimLead = 0;
  2856. float currentLead = leadingPaddingAndBorderCross;
  2857. if (!YGFloatIsUndefined(availableInnerCrossDim)) {
  2858. const float remainingAlignContentDim =
  2859. availableInnerCrossDim - totalLineCrossDim;
  2860. switch (node->getStyle().alignContent) {
  2861. case YGAlignFlexEnd:
  2862. currentLead += remainingAlignContentDim;
  2863. break;
  2864. case YGAlignCenter:
  2865. currentLead += remainingAlignContentDim / 2;
  2866. break;
  2867. case YGAlignStretch:
  2868. if (availableInnerCrossDim > totalLineCrossDim) {
  2869. crossDimLead = remainingAlignContentDim / lineCount;
  2870. }
  2871. break;
  2872. case YGAlignSpaceAround:
  2873. if (availableInnerCrossDim > totalLineCrossDim) {
  2874. currentLead += remainingAlignContentDim / (2 * lineCount);
  2875. if (lineCount > 1) {
  2876. crossDimLead = remainingAlignContentDim / lineCount;
  2877. }
  2878. } else {
  2879. currentLead += remainingAlignContentDim / 2;
  2880. }
  2881. break;
  2882. case YGAlignSpaceBetween:
  2883. if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {
  2884. crossDimLead = remainingAlignContentDim / (lineCount - 1);
  2885. }
  2886. break;
  2887. case YGAlignAuto:
  2888. case YGAlignFlexStart:
  2889. case YGAlignBaseline:
  2890. break;
  2891. }
  2892. }
  2893. uint32_t endIndex = 0;
  2894. for (uint32_t i = 0; i < lineCount; i++) {
  2895. const uint32_t startIndex = endIndex;
  2896. uint32_t ii;
  2897. // compute the line's height and find the endIndex
  2898. float lineHeight = 0;
  2899. float maxAscentForCurrentLine = 0;
  2900. float maxDescentForCurrentLine = 0;
  2901. for (ii = startIndex; ii < childCount; ii++) {
  2902. const YGNodeRef child = node->getChild(ii);
  2903. if (child->getStyle().display == YGDisplayNone) {
  2904. continue;
  2905. }
  2906. if (child->getStyle().positionType == YGPositionTypeRelative) {
  2907. if (child->getLineIndex() != i) {
  2908. break;
  2909. }
  2910. if (YGNodeIsLayoutDimDefined(child, crossAxis)) {
  2911. lineHeight = YGFloatMax(
  2912. lineHeight,
  2913. child->getLayout().measuredDimensions[dim[crossAxis]] +
  2914. child->getMarginForAxis(crossAxis, availableInnerWidth)
  2915. .unwrap());
  2916. }
  2917. if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
  2918. const float ascent = YGBaseline(child, layoutContext) +
  2919. child
  2920. ->getLeadingMargin(
  2921. YGFlexDirectionColumn, availableInnerWidth)
  2922. .unwrap();
  2923. const float descent =
  2924. child->getLayout().measuredDimensions[YGDimensionHeight] +
  2925. child
  2926. ->getMarginForAxis(
  2927. YGFlexDirectionColumn, availableInnerWidth)
  2928. .unwrap() -
  2929. ascent;
  2930. maxAscentForCurrentLine =
  2931. YGFloatMax(maxAscentForCurrentLine, ascent);
  2932. maxDescentForCurrentLine =
  2933. YGFloatMax(maxDescentForCurrentLine, descent);
  2934. lineHeight = YGFloatMax(
  2935. lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);
  2936. }
  2937. }
  2938. }
  2939. endIndex = ii;
  2940. lineHeight += crossDimLead;
  2941. if (performLayout) {
  2942. for (ii = startIndex; ii < endIndex; ii++) {
  2943. const YGNodeRef child = node->getChild(ii);
  2944. if (child->getStyle().display == YGDisplayNone) {
  2945. continue;
  2946. }
  2947. if (child->getStyle().positionType == YGPositionTypeRelative) {
  2948. switch (YGNodeAlignItem(node, child)) {
  2949. case YGAlignFlexStart: {
  2950. child->setLayoutPosition(
  2951. currentLead +
  2952. child->getLeadingMargin(crossAxis, availableInnerWidth)
  2953. .unwrap(),
  2954. pos[crossAxis]);
  2955. break;
  2956. }
  2957. case YGAlignFlexEnd: {
  2958. child->setLayoutPosition(
  2959. currentLead + lineHeight -
  2960. child->getTrailingMargin(crossAxis, availableInnerWidth)
  2961. .unwrap() -
  2962. child->getLayout().measuredDimensions[dim[crossAxis]],
  2963. pos[crossAxis]);
  2964. break;
  2965. }
  2966. case YGAlignCenter: {
  2967. float childHeight =
  2968. child->getLayout().measuredDimensions[dim[crossAxis]];
  2969. child->setLayoutPosition(
  2970. currentLead + (lineHeight - childHeight) / 2,
  2971. pos[crossAxis]);
  2972. break;
  2973. }
  2974. case YGAlignStretch: {
  2975. child->setLayoutPosition(
  2976. currentLead +
  2977. child->getLeadingMargin(crossAxis, availableInnerWidth)
  2978. .unwrap(),
  2979. pos[crossAxis]);
  2980. // Remeasure child with the line height as it as been only
  2981. // measured with the owners height yet.
  2982. if (!YGNodeIsStyleDimDefined(
  2983. child, crossAxis, availableInnerCrossDim)) {
  2984. const float childWidth = isMainAxisRow
  2985. ? (child->getLayout()
  2986. .measuredDimensions[YGDimensionWidth] +
  2987. child->getMarginForAxis(mainAxis, availableInnerWidth)
  2988. .unwrap())
  2989. : lineHeight;
  2990. const float childHeight = !isMainAxisRow
  2991. ? (child->getLayout()
  2992. .measuredDimensions[YGDimensionHeight] +
  2993. child->getMarginForAxis(crossAxis, availableInnerWidth)
  2994. .unwrap())
  2995. : lineHeight;
  2996. if (!(YGFloatsEqual(
  2997. childWidth,
  2998. child->getLayout()
  2999. .measuredDimensions[YGDimensionWidth]) &&
  3000. YGFloatsEqual(
  3001. childHeight,
  3002. child->getLayout()
  3003. .measuredDimensions[YGDimensionHeight]))) {
  3004. YGLayoutNodeInternal(
  3005. child,
  3006. childWidth,
  3007. childHeight,
  3008. direction,
  3009. YGMeasureModeExactly,
  3010. YGMeasureModeExactly,
  3011. availableInnerWidth,
  3012. availableInnerHeight,
  3013. true,
  3014. "multiline-stretch",
  3015. config,
  3016. layoutMarkerData,
  3017. layoutContext);
  3018. }
  3019. }
  3020. break;
  3021. }
  3022. case YGAlignBaseline: {
  3023. child->setLayoutPosition(
  3024. currentLead + maxAscentForCurrentLine -
  3025. YGBaseline(child, layoutContext) +
  3026. child
  3027. ->getLeadingPosition(
  3028. YGFlexDirectionColumn, availableInnerCrossDim)
  3029. .unwrap(),
  3030. YGEdgeTop);
  3031. break;
  3032. }
  3033. case YGAlignAuto:
  3034. case YGAlignSpaceBetween:
  3035. case YGAlignSpaceAround:
  3036. break;
  3037. }
  3038. }
  3039. }
  3040. }
  3041. currentLead += lineHeight;
  3042. }
  3043. }
  3044. // STEP 9: COMPUTING FINAL DIMENSIONS
  3045. node->setLayoutMeasuredDimension(
  3046. YGNodeBoundAxis(
  3047. node,
  3048. YGFlexDirectionRow,
  3049. availableWidth - marginAxisRow,
  3050. ownerWidth,
  3051. ownerWidth),
  3052. YGDimensionWidth);
  3053. node->setLayoutMeasuredDimension(
  3054. YGNodeBoundAxis(
  3055. node,
  3056. YGFlexDirectionColumn,
  3057. availableHeight - marginAxisColumn,
  3058. ownerHeight,
  3059. ownerWidth),
  3060. YGDimensionHeight);
  3061. // If the user didn't specify a width or height for the node, set the
  3062. // dimensions based on the children.
  3063. if (measureModeMainDim == YGMeasureModeUndefined ||
  3064. (node->getStyle().overflow != YGOverflowScroll &&
  3065. measureModeMainDim == YGMeasureModeAtMost)) {
  3066. // Clamp the size to the min/max size, if specified, and make sure it
  3067. // doesn't go below the padding and border amount.
  3068. node->setLayoutMeasuredDimension(
  3069. YGNodeBoundAxis(
  3070. node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth),
  3071. dim[mainAxis]);
  3072. } else if (
  3073. measureModeMainDim == YGMeasureModeAtMost &&
  3074. node->getStyle().overflow == YGOverflowScroll) {
  3075. node->setLayoutMeasuredDimension(
  3076. YGFloatMax(
  3077. YGFloatMin(
  3078. availableInnerMainDim + paddingAndBorderAxisMain,
  3079. YGNodeBoundAxisWithinMinAndMax(
  3080. node,
  3081. mainAxis,
  3082. YGFloatOptional{maxLineMainDim},
  3083. mainAxisownerSize)
  3084. .unwrap()),
  3085. paddingAndBorderAxisMain),
  3086. dim[mainAxis]);
  3087. }
  3088. if (measureModeCrossDim == YGMeasureModeUndefined ||
  3089. (node->getStyle().overflow != YGOverflowScroll &&
  3090. measureModeCrossDim == YGMeasureModeAtMost)) {
  3091. // Clamp the size to the min/max size, if specified, and make sure it
  3092. // doesn't go below the padding and border amount.
  3093. node->setLayoutMeasuredDimension(
  3094. YGNodeBoundAxis(
  3095. node,
  3096. crossAxis,
  3097. totalLineCrossDim + paddingAndBorderAxisCross,
  3098. crossAxisownerSize,
  3099. ownerWidth),
  3100. dim[crossAxis]);
  3101. } else if (
  3102. measureModeCrossDim == YGMeasureModeAtMost &&
  3103. node->getStyle().overflow == YGOverflowScroll) {
  3104. node->setLayoutMeasuredDimension(
  3105. YGFloatMax(
  3106. YGFloatMin(
  3107. availableInnerCrossDim + paddingAndBorderAxisCross,
  3108. YGNodeBoundAxisWithinMinAndMax(
  3109. node,
  3110. crossAxis,
  3111. YGFloatOptional{totalLineCrossDim +
  3112. paddingAndBorderAxisCross},
  3113. crossAxisownerSize)
  3114. .unwrap()),
  3115. paddingAndBorderAxisCross),
  3116. dim[crossAxis]);
  3117. }
  3118. // As we only wrapped in normal direction yet, we need to reverse the
  3119. // positions on wrap-reverse.
  3120. if (performLayout && node->getStyle().flexWrap == YGWrapWrapReverse) {
  3121. for (uint32_t i = 0; i < childCount; i++) {
  3122. const YGNodeRef child = YGNodeGetChild(node, i);
  3123. if (child->getStyle().positionType == YGPositionTypeRelative) {
  3124. child->setLayoutPosition(
  3125. node->getLayout().measuredDimensions[dim[crossAxis]] -
  3126. child->getLayout().position[pos[crossAxis]] -
  3127. child->getLayout().measuredDimensions[dim[crossAxis]],
  3128. pos[crossAxis]);
  3129. }
  3130. }
  3131. }
  3132. if (performLayout) {
  3133. // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN
  3134. for (auto child : node->getChildren()) {
  3135. if (child->getStyle().positionType != YGPositionTypeAbsolute) {
  3136. continue;
  3137. }
  3138. YGNodeAbsoluteLayoutChild(
  3139. node,
  3140. child,
  3141. availableInnerWidth,
  3142. isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
  3143. availableInnerHeight,
  3144. direction,
  3145. config,
  3146. layoutMarkerData,
  3147. layoutContext);
  3148. }
  3149. // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN
  3150. const bool needsMainTrailingPos = mainAxis == YGFlexDirectionRowReverse ||
  3151. mainAxis == YGFlexDirectionColumnReverse;
  3152. const bool needsCrossTrailingPos = crossAxis == YGFlexDirectionRowReverse ||
  3153. crossAxis == YGFlexDirectionColumnReverse;
  3154. // Set trailing position if necessary.
  3155. if (needsMainTrailingPos || needsCrossTrailingPos) {
  3156. for (uint32_t i = 0; i < childCount; i++) {
  3157. const YGNodeRef child = node->getChild(i);
  3158. if (child->getStyle().display == YGDisplayNone) {
  3159. continue;
  3160. }
  3161. if (needsMainTrailingPos) {
  3162. YGNodeSetChildTrailingPosition(node, child, mainAxis);
  3163. }
  3164. if (needsCrossTrailingPos) {
  3165. YGNodeSetChildTrailingPosition(node, child, crossAxis);
  3166. }
  3167. }
  3168. }
  3169. }
  3170. }
  3171. uint32_t gDepth = 0;
  3172. bool gPrintChanges = false;
  3173. bool gPrintSkips = false;
  3174. static const char* spacer =
  3175. " ";
  3176. static const char* YGSpacer(const unsigned long level) {
  3177. const size_t spacerLen = strlen(spacer);
  3178. if (level > spacerLen) {
  3179. return &spacer[0];
  3180. } else {
  3181. return &spacer[spacerLen - level];
  3182. }
  3183. }
  3184. static const char* YGMeasureModeName(
  3185. const YGMeasureMode mode,
  3186. const bool performLayout) {
  3187. constexpr auto N = enums::count<YGMeasureMode>();
  3188. const char* kMeasureModeNames[N] = {"UNDEFINED", "EXACTLY", "AT_MOST"};
  3189. const char* kLayoutModeNames[N] = {
  3190. "LAY_UNDEFINED", "LAY_EXACTLY", "LAY_AT_MOST"};
  3191. if (mode >= N) {
  3192. return "";
  3193. }
  3194. return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode];
  3195. }
  3196. static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
  3197. YGMeasureMode sizeMode,
  3198. float size,
  3199. float lastComputedSize) {
  3200. return sizeMode == YGMeasureModeExactly &&
  3201. YGFloatsEqual(size, lastComputedSize);
  3202. }
  3203. static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
  3204. YGMeasureMode sizeMode,
  3205. float size,
  3206. YGMeasureMode lastSizeMode,
  3207. float lastComputedSize) {
  3208. return sizeMode == YGMeasureModeAtMost &&
  3209. lastSizeMode == YGMeasureModeUndefined &&
  3210. (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize));
  3211. }
  3212. static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
  3213. YGMeasureMode sizeMode,
  3214. float size,
  3215. YGMeasureMode lastSizeMode,
  3216. float lastSize,
  3217. float lastComputedSize) {
  3218. return lastSizeMode == YGMeasureModeAtMost &&
  3219. sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) &&
  3220. !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) &&
  3221. lastSize > size &&
  3222. (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize));
  3223. }
  3224. float YGRoundValueToPixelGrid(
  3225. const float value,
  3226. const float pointScaleFactor,
  3227. const bool forceCeil,
  3228. const bool forceFloor) {
  3229. float scaledValue = value * pointScaleFactor;
  3230. // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue
  3231. // - fractial`.
  3232. float fractial = fmodf(scaledValue, 1.0f);
  3233. if (fractial < 0) {
  3234. // This branch is for handling negative numbers for `value`.
  3235. //
  3236. // Regarding `floor` and `ceil`. Note that for a number x, `floor(x) <= x <=
  3237. // ceil(x)` even for negative numbers. Here are a couple of examples:
  3238. // - x = 2.2: floor( 2.2) = 2, ceil( 2.2) = 3
  3239. // - x = -2.2: floor(-2.2) = -3, ceil(-2.2) = -2
  3240. //
  3241. // Regarding `fmodf`. For fractional negative numbers, `fmodf` returns a
  3242. // negative number. For example, `fmodf(-2.2) = -0.2`. However, we want
  3243. // `fractial` to be the number such that subtracting it from `value` will
  3244. // give us `floor(value)`. In the case of negative numbers, adding 1 to
  3245. // `fmodf(value)` gives us this. Let's continue the example from above:
  3246. // - fractial = fmodf(-2.2) = -0.2
  3247. // - Add 1 to the fraction: fractial2 = fractial + 1 = -0.2 + 1 = 0.8
  3248. // - Finding the `floor`: -2.2 - fractial2 = -2.2 - 0.8 = -3
  3249. ++fractial;
  3250. }
  3251. if (YGFloatsEqual(fractial, 0)) {
  3252. // First we check if the value is already rounded
  3253. scaledValue = scaledValue - fractial;
  3254. } else if (YGFloatsEqual(fractial, 1.0f)) {
  3255. scaledValue = scaledValue - fractial + 1.0f;
  3256. } else if (forceCeil) {
  3257. // Next we check if we need to use forced rounding
  3258. scaledValue = scaledValue - fractial + 1.0f;
  3259. } else if (forceFloor) {
  3260. scaledValue = scaledValue - fractial;
  3261. } else {
  3262. // Finally we just round the value
  3263. scaledValue = scaledValue - fractial +
  3264. (!YGFloatIsUndefined(fractial) &&
  3265. (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f))
  3266. ? 1.0f
  3267. : 0.0f);
  3268. }
  3269. return (YGFloatIsUndefined(scaledValue) ||
  3270. YGFloatIsUndefined(pointScaleFactor))
  3271. ? YGUndefined
  3272. : scaledValue / pointScaleFactor;
  3273. }
  3274. bool YGNodeCanUseCachedMeasurement(
  3275. const YGMeasureMode widthMode,
  3276. const float width,
  3277. const YGMeasureMode heightMode,
  3278. const float height,
  3279. const YGMeasureMode lastWidthMode,
  3280. const float lastWidth,
  3281. const YGMeasureMode lastHeightMode,
  3282. const float lastHeight,
  3283. const float lastComputedWidth,
  3284. const float lastComputedHeight,
  3285. const float marginRow,
  3286. const float marginColumn,
  3287. const YGConfigRef config) {
  3288. if ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) ||
  3289. (!YGFloatIsUndefined(lastComputedWidth) && lastComputedWidth < 0)) {
  3290. return false;
  3291. }
  3292. bool useRoundedComparison =
  3293. config != nullptr && config->pointScaleFactor != 0;
  3294. const float effectiveWidth = useRoundedComparison
  3295. ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false)
  3296. : width;
  3297. const float effectiveHeight = useRoundedComparison
  3298. ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false)
  3299. : height;
  3300. const float effectiveLastWidth = useRoundedComparison
  3301. ? YGRoundValueToPixelGrid(
  3302. lastWidth, config->pointScaleFactor, false, false)
  3303. : lastWidth;
  3304. const float effectiveLastHeight = useRoundedComparison
  3305. ? YGRoundValueToPixelGrid(
  3306. lastHeight, config->pointScaleFactor, false, false)
  3307. : lastHeight;
  3308. const bool hasSameWidthSpec = lastWidthMode == widthMode &&
  3309. YGFloatsEqual(effectiveLastWidth, effectiveWidth);
  3310. const bool hasSameHeightSpec = lastHeightMode == heightMode &&
  3311. YGFloatsEqual(effectiveLastHeight, effectiveHeight);
  3312. const bool widthIsCompatible =
  3313. hasSameWidthSpec ||
  3314. YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
  3315. widthMode, width - marginRow, lastComputedWidth) ||
  3316. YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
  3317. widthMode, width - marginRow, lastWidthMode, lastComputedWidth) ||
  3318. YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
  3319. widthMode,
  3320. width - marginRow,
  3321. lastWidthMode,
  3322. lastWidth,
  3323. lastComputedWidth);
  3324. const bool heightIsCompatible =
  3325. hasSameHeightSpec ||
  3326. YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
  3327. heightMode, height - marginColumn, lastComputedHeight) ||
  3328. YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
  3329. heightMode,
  3330. height - marginColumn,
  3331. lastHeightMode,
  3332. lastComputedHeight) ||
  3333. YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
  3334. heightMode,
  3335. height - marginColumn,
  3336. lastHeightMode,
  3337. lastHeight,
  3338. lastComputedHeight);
  3339. return widthIsCompatible && heightIsCompatible;
  3340. }
  3341. //
  3342. // This is a wrapper around the YGNodelayoutImpl function. It determines whether
  3343. // the layout request is redundant and can be skipped.
  3344. //
  3345. // Parameters:
  3346. // Input parameters are the same as YGNodelayoutImpl (see above)
  3347. // Return parameter is true if layout was performed, false if skipped
  3348. //
  3349. bool YGLayoutNodeInternal(
  3350. const YGNodeRef node,
  3351. const float availableWidth,
  3352. const float availableHeight,
  3353. const YGDirection ownerDirection,
  3354. const YGMeasureMode widthMeasureMode,
  3355. const YGMeasureMode heightMeasureMode,
  3356. const float ownerWidth,
  3357. const float ownerHeight,
  3358. const bool performLayout,
  3359. const char* reason,
  3360. const YGConfigRef config,
  3361. YGMarkerLayoutData& layoutMarkerData,
  3362. void* const layoutContext) {
  3363. YGLayout* layout = &node->getLayout();
  3364. gDepth++;
  3365. const bool needToVisitNode =
  3366. (node->isDirty() && layout->generationCount != gCurrentGenerationCount) ||
  3367. layout->lastOwnerDirection != ownerDirection;
  3368. if (needToVisitNode) {
  3369. // Invalidate the cached results.
  3370. layout->nextCachedMeasurementsIndex = 0;
  3371. layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1;
  3372. layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1;
  3373. layout->cachedLayout.computedWidth = -1;
  3374. layout->cachedLayout.computedHeight = -1;
  3375. }
  3376. YGCachedMeasurement* cachedResults = nullptr;
  3377. // Determine whether the results are already cached. We maintain a separate
  3378. // cache for layouts and measurements. A layout operation modifies the
  3379. // positions and dimensions for nodes in the subtree. The algorithm assumes
  3380. // that each node gets layed out a maximum of one time per tree layout, but
  3381. // multiple measurements may be required to resolve all of the flex
  3382. // dimensions. We handle nodes with measure functions specially here because
  3383. // they are the most expensive to measure, so it's worth avoiding redundant
  3384. // measurements if at all possible.
  3385. if (node->hasMeasureFunc()) {
  3386. const float marginAxisRow =
  3387. node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
  3388. const float marginAxisColumn =
  3389. node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
  3390. // First, try to use the layout cache.
  3391. if (YGNodeCanUseCachedMeasurement(
  3392. widthMeasureMode,
  3393. availableWidth,
  3394. heightMeasureMode,
  3395. availableHeight,
  3396. layout->cachedLayout.widthMeasureMode,
  3397. layout->cachedLayout.availableWidth,
  3398. layout->cachedLayout.heightMeasureMode,
  3399. layout->cachedLayout.availableHeight,
  3400. layout->cachedLayout.computedWidth,
  3401. layout->cachedLayout.computedHeight,
  3402. marginAxisRow,
  3403. marginAxisColumn,
  3404. config)) {
  3405. cachedResults = &layout->cachedLayout;
  3406. } else {
  3407. // Try to use the measurement cache.
  3408. for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
  3409. if (YGNodeCanUseCachedMeasurement(
  3410. widthMeasureMode,
  3411. availableWidth,
  3412. heightMeasureMode,
  3413. availableHeight,
  3414. layout->cachedMeasurements[i].widthMeasureMode,
  3415. layout->cachedMeasurements[i].availableWidth,
  3416. layout->cachedMeasurements[i].heightMeasureMode,
  3417. layout->cachedMeasurements[i].availableHeight,
  3418. layout->cachedMeasurements[i].computedWidth,
  3419. layout->cachedMeasurements[i].computedHeight,
  3420. marginAxisRow,
  3421. marginAxisColumn,
  3422. config)) {
  3423. cachedResults = &layout->cachedMeasurements[i];
  3424. break;
  3425. }
  3426. }
  3427. }
  3428. } else if (performLayout) {
  3429. if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&
  3430. YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&
  3431. layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
  3432. layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
  3433. cachedResults = &layout->cachedLayout;
  3434. }
  3435. } else {
  3436. for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
  3437. if (YGFloatsEqual(
  3438. layout->cachedMeasurements[i].availableWidth, availableWidth) &&
  3439. YGFloatsEqual(
  3440. layout->cachedMeasurements[i].availableHeight, availableHeight) &&
  3441. layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
  3442. layout->cachedMeasurements[i].heightMeasureMode ==
  3443. heightMeasureMode) {
  3444. cachedResults = &layout->cachedMeasurements[i];
  3445. break;
  3446. }
  3447. }
  3448. }
  3449. if (!needToVisitNode && cachedResults != nullptr) {
  3450. layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;
  3451. layout->measuredDimensions[YGDimensionHeight] =
  3452. cachedResults->computedHeight;
  3453. (performLayout ? layoutMarkerData.cachedLayouts
  3454. : layoutMarkerData.cachedMeasures) += 1;
  3455. if (gPrintChanges && gPrintSkips) {
  3456. Log::log(
  3457. node,
  3458. YGLogLevelVerbose,
  3459. nullptr,
  3460. "%s%d.{[skipped] ",
  3461. YGSpacer(gDepth),
  3462. gDepth);
  3463. node->print(layoutContext);
  3464. Log::log(
  3465. node,
  3466. YGLogLevelVerbose,
  3467. nullptr,
  3468. "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
  3469. YGMeasureModeName(widthMeasureMode, performLayout),
  3470. YGMeasureModeName(heightMeasureMode, performLayout),
  3471. availableWidth,
  3472. availableHeight,
  3473. cachedResults->computedWidth,
  3474. cachedResults->computedHeight,
  3475. reason);
  3476. }
  3477. } else {
  3478. if (gPrintChanges) {
  3479. Log::log(
  3480. node,
  3481. YGLogLevelVerbose,
  3482. nullptr,
  3483. "%s%d.{%s",
  3484. YGSpacer(gDepth),
  3485. gDepth,
  3486. needToVisitNode ? "*" : "");
  3487. node->print(layoutContext);
  3488. Log::log(
  3489. node,
  3490. YGLogLevelVerbose,
  3491. nullptr,
  3492. "wm: %s, hm: %s, aw: %f ah: %f %s\n",
  3493. YGMeasureModeName(widthMeasureMode, performLayout),
  3494. YGMeasureModeName(heightMeasureMode, performLayout),
  3495. availableWidth,
  3496. availableHeight,
  3497. reason);
  3498. }
  3499. YGNodelayoutImpl(
  3500. node,
  3501. availableWidth,
  3502. availableHeight,
  3503. ownerDirection,
  3504. widthMeasureMode,
  3505. heightMeasureMode,
  3506. ownerWidth,
  3507. ownerHeight,
  3508. performLayout,
  3509. config,
  3510. layoutMarkerData,
  3511. layoutContext);
  3512. if (gPrintChanges) {
  3513. Log::log(
  3514. node,
  3515. YGLogLevelVerbose,
  3516. nullptr,
  3517. "%s%d.}%s",
  3518. YGSpacer(gDepth),
  3519. gDepth,
  3520. needToVisitNode ? "*" : "");
  3521. node->print(layoutContext);
  3522. Log::log(
  3523. node,
  3524. YGLogLevelVerbose,
  3525. nullptr,
  3526. "wm: %s, hm: %s, d: (%f, %f) %s\n",
  3527. YGMeasureModeName(widthMeasureMode, performLayout),
  3528. YGMeasureModeName(heightMeasureMode, performLayout),
  3529. layout->measuredDimensions[YGDimensionWidth],
  3530. layout->measuredDimensions[YGDimensionHeight],
  3531. reason);
  3532. }
  3533. layout->lastOwnerDirection = ownerDirection;
  3534. if (cachedResults == nullptr) {
  3535. if (layout->nextCachedMeasurementsIndex + 1 >
  3536. (uint32_t) layoutMarkerData.maxMeasureCache) {
  3537. layoutMarkerData.maxMeasureCache =
  3538. layout->nextCachedMeasurementsIndex + 1;
  3539. }
  3540. if (layout->nextCachedMeasurementsIndex == usedMeasureCacheEntries) {
  3541. if (gPrintChanges) {
  3542. Log::log(node, YGLogLevelVerbose, nullptr, "Out of cache entries!\n");
  3543. }
  3544. layout->nextCachedMeasurementsIndex = 0;
  3545. }
  3546. YGCachedMeasurement* newCacheEntry;
  3547. if (performLayout) {
  3548. // Use the single layout cache entry.
  3549. newCacheEntry = &layout->cachedLayout;
  3550. } else {
  3551. // Allocate a new measurement cache entry.
  3552. newCacheEntry =
  3553. &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
  3554. layout->nextCachedMeasurementsIndex++;
  3555. }
  3556. newCacheEntry->availableWidth = availableWidth;
  3557. newCacheEntry->availableHeight = availableHeight;
  3558. newCacheEntry->widthMeasureMode = widthMeasureMode;
  3559. newCacheEntry->heightMeasureMode = heightMeasureMode;
  3560. newCacheEntry->computedWidth =
  3561. layout->measuredDimensions[YGDimensionWidth];
  3562. newCacheEntry->computedHeight =
  3563. layout->measuredDimensions[YGDimensionHeight];
  3564. }
  3565. }
  3566. if (performLayout) {
  3567. node->setLayoutDimension(
  3568. node->getLayout().measuredDimensions[YGDimensionWidth],
  3569. YGDimensionWidth);
  3570. node->setLayoutDimension(
  3571. node->getLayout().measuredDimensions[YGDimensionHeight],
  3572. YGDimensionHeight);
  3573. node->setHasNewLayout(true);
  3574. node->setDirty(false);
  3575. }
  3576. gDepth--;
  3577. layout->generationCount = gCurrentGenerationCount;
  3578. return (needToVisitNode || cachedResults == nullptr);
  3579. }
  3580. void YGConfigSetPointScaleFactor(
  3581. const YGConfigRef config,
  3582. const float pixelsInPoint) {
  3583. YGAssertWithConfig(
  3584. config,
  3585. pixelsInPoint >= 0.0f,
  3586. "Scale factor should not be less than zero");
  3587. // We store points for Pixel as we will use it for rounding
  3588. if (pixelsInPoint == 0.0f) {
  3589. // Zero is used to skip rounding
  3590. config->pointScaleFactor = 0.0f;
  3591. } else {
  3592. config->pointScaleFactor = pixelsInPoint;
  3593. }
  3594. }
  3595. static void YGRoundToPixelGrid(
  3596. const YGNodeRef node,
  3597. const float pointScaleFactor,
  3598. const float absoluteLeft,
  3599. const float absoluteTop) {
  3600. if (pointScaleFactor == 0.0f) {
  3601. return;
  3602. }
  3603. const float nodeLeft = node->getLayout().position[YGEdgeLeft];
  3604. const float nodeTop = node->getLayout().position[YGEdgeTop];
  3605. const float nodeWidth = node->getLayout().dimensions[YGDimensionWidth];
  3606. const float nodeHeight = node->getLayout().dimensions[YGDimensionHeight];
  3607. const float absoluteNodeLeft = absoluteLeft + nodeLeft;
  3608. const float absoluteNodeTop = absoluteTop + nodeTop;
  3609. const float absoluteNodeRight = absoluteNodeLeft + nodeWidth;
  3610. const float absoluteNodeBottom = absoluteNodeTop + nodeHeight;
  3611. // If a node has a custom measure function we never want to round down its
  3612. // size as this could lead to unwanted text truncation.
  3613. const bool textRounding = node->getNodeType() == YGNodeTypeText;
  3614. node->setLayoutPosition(
  3615. YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
  3616. YGEdgeLeft);
  3617. node->setLayoutPosition(
  3618. YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
  3619. YGEdgeTop);
  3620. // We multiply dimension by scale factor and if the result is close to the
  3621. // whole number, we don't have any fraction To verify if the result is close
  3622. // to whole number we want to check both floor and ceil numbers
  3623. const bool hasFractionalWidth =
  3624. !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) &&
  3625. !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0);
  3626. const bool hasFractionalHeight =
  3627. !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) &&
  3628. !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0);
  3629. node->setLayoutDimension(
  3630. YGRoundValueToPixelGrid(
  3631. absoluteNodeRight,
  3632. pointScaleFactor,
  3633. (textRounding && hasFractionalWidth),
  3634. (textRounding && !hasFractionalWidth)) -
  3635. YGRoundValueToPixelGrid(
  3636. absoluteNodeLeft, pointScaleFactor, false, textRounding),
  3637. YGDimensionWidth);
  3638. node->setLayoutDimension(
  3639. YGRoundValueToPixelGrid(
  3640. absoluteNodeBottom,
  3641. pointScaleFactor,
  3642. (textRounding && hasFractionalHeight),
  3643. (textRounding && !hasFractionalHeight)) -
  3644. YGRoundValueToPixelGrid(
  3645. absoluteNodeTop, pointScaleFactor, false, textRounding),
  3646. YGDimensionHeight);
  3647. const uint32_t childCount = YGNodeGetChildCount(node);
  3648. for (uint32_t i = 0; i < childCount; i++) {
  3649. YGRoundToPixelGrid(
  3650. YGNodeGetChild(node, i),
  3651. pointScaleFactor,
  3652. absoluteNodeLeft,
  3653. absoluteNodeTop);
  3654. }
  3655. }
  3656. void YGNodeCalculateLayoutWithContext(
  3657. const YGNodeRef node,
  3658. const float ownerWidth,
  3659. const float ownerHeight,
  3660. const YGDirection ownerDirection,
  3661. void* layoutContext) {
  3662. marker::MarkerSection<YGMarkerLayout> marker{node};
  3663. // Increment the generation count. This will force the recursive routine to
  3664. // visit all dirty nodes at least once. Subsequent visits will be skipped if
  3665. // the input parameters don't change.
  3666. gCurrentGenerationCount++;
  3667. node->resolveDimension();
  3668. float width = YGUndefined;
  3669. YGMeasureMode widthMeasureMode = YGMeasureModeUndefined;
  3670. if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, ownerWidth)) {
  3671. width =
  3672. (YGResolveValue(
  3673. node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) +
  3674. node->getMarginForAxis(YGFlexDirectionRow, ownerWidth))
  3675. .unwrap();
  3676. widthMeasureMode = YGMeasureModeExactly;
  3677. } else if (!YGResolveValue(
  3678. node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)
  3679. .isUndefined()) {
  3680. width = YGResolveValue(
  3681. node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)
  3682. .unwrap();
  3683. widthMeasureMode = YGMeasureModeAtMost;
  3684. } else {
  3685. width = ownerWidth;
  3686. widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined
  3687. : YGMeasureModeExactly;
  3688. }
  3689. float height = YGUndefined;
  3690. YGMeasureMode heightMeasureMode = YGMeasureModeUndefined;
  3691. if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) {
  3692. height = (YGResolveValue(
  3693. node->getResolvedDimension(dim[YGFlexDirectionColumn]),
  3694. ownerHeight) +
  3695. node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth))
  3696. .unwrap();
  3697. heightMeasureMode = YGMeasureModeExactly;
  3698. } else if (!YGResolveValue(
  3699. node->getStyle().maxDimensions[YGDimensionHeight],
  3700. ownerHeight)
  3701. .isUndefined()) {
  3702. height = YGResolveValue(
  3703. node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight)
  3704. .unwrap();
  3705. heightMeasureMode = YGMeasureModeAtMost;
  3706. } else {
  3707. height = ownerHeight;
  3708. heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined
  3709. : YGMeasureModeExactly;
  3710. }
  3711. if (YGLayoutNodeInternal(
  3712. node,
  3713. width,
  3714. height,
  3715. ownerDirection,
  3716. widthMeasureMode,
  3717. heightMeasureMode,
  3718. ownerWidth,
  3719. ownerHeight,
  3720. true,
  3721. "initial",
  3722. node->getConfig(),
  3723. marker.data,
  3724. layoutContext)) {
  3725. node->setPosition(
  3726. node->getLayout().direction, ownerWidth, ownerHeight, ownerWidth);
  3727. YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f);
  3728. #ifdef DEBUG
  3729. if (node->getConfig()->printTree) {
  3730. YGNodePrint(
  3731. node,
  3732. (YGPrintOptions)(
  3733. YGPrintOptionsLayout | YGPrintOptionsChildren |
  3734. YGPrintOptionsStyle));
  3735. }
  3736. #endif
  3737. }
  3738. // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we
  3739. // aren't sure whether client's of yoga have gotten rid off this flag or not.
  3740. // So logging this in YGLayout would help to find out the call sites depending
  3741. // on this flag. This check would be removed once we are sure no one is
  3742. // dependent on this flag anymore. The flag
  3743. // `shouldDiffLayoutWithoutLegacyStretchBehaviour` in YGConfig will help to
  3744. // run experiments.
  3745. if (node->getConfig()->shouldDiffLayoutWithoutLegacyStretchBehaviour &&
  3746. node->didUseLegacyFlag()) {
  3747. const YGNodeRef originalNode = YGNodeDeepClone(node);
  3748. originalNode->resolveDimension();
  3749. // Recursively mark nodes as dirty
  3750. originalNode->markDirtyAndPropogateDownwards();
  3751. gCurrentGenerationCount++;
  3752. // Rerun the layout, and calculate the diff
  3753. originalNode->setAndPropogateUseLegacyFlag(false);
  3754. YGMarkerLayoutData layoutMarkerData;
  3755. if (YGLayoutNodeInternal(
  3756. originalNode,
  3757. width,
  3758. height,
  3759. ownerDirection,
  3760. widthMeasureMode,
  3761. heightMeasureMode,
  3762. ownerWidth,
  3763. ownerHeight,
  3764. true,
  3765. "initial",
  3766. originalNode->getConfig(),
  3767. layoutMarkerData,
  3768. layoutContext)) {
  3769. originalNode->setPosition(
  3770. originalNode->getLayout().direction,
  3771. ownerWidth,
  3772. ownerHeight,
  3773. ownerWidth);
  3774. YGRoundToPixelGrid(
  3775. originalNode,
  3776. originalNode->getConfig()->pointScaleFactor,
  3777. 0.0f,
  3778. 0.0f);
  3779. // Set whether the two layouts are different or not.
  3780. auto neededLegacyStretchBehaviour =
  3781. !originalNode->isLayoutTreeEqualToNode(*node);
  3782. node->setLayoutDoesLegacyFlagAffectsLayout(neededLegacyStretchBehaviour);
  3783. #ifdef DEBUG
  3784. if (originalNode->getConfig()->printTree) {
  3785. YGNodePrint(
  3786. originalNode,
  3787. (YGPrintOptions)(
  3788. YGPrintOptionsLayout | YGPrintOptionsChildren |
  3789. YGPrintOptionsStyle));
  3790. }
  3791. #endif
  3792. }
  3793. YGConfigFreeRecursive(originalNode);
  3794. YGNodeFreeRecursive(originalNode);
  3795. }
  3796. }
  3797. void YGNodeCalculateLayout(
  3798. const YGNodeRef node,
  3799. const float ownerWidth,
  3800. const float ownerHeight,
  3801. const YGDirection ownerDirection) {
  3802. YGNodeCalculateLayoutWithContext(
  3803. node, ownerWidth, ownerHeight, ownerDirection, nullptr);
  3804. }
  3805. void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) {
  3806. if (logger != nullptr) {
  3807. config->setLogger(logger);
  3808. } else {
  3809. #ifdef ANDROID
  3810. config->setLogger(&YGAndroidLog);
  3811. #else
  3812. config->setLogger(&YGDefaultLog);
  3813. #endif
  3814. }
  3815. }
  3816. void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour(
  3817. const YGConfigRef config,
  3818. const bool shouldDiffLayout) {
  3819. config->shouldDiffLayoutWithoutLegacyStretchBehaviour = shouldDiffLayout;
  3820. }
  3821. void YGAssert(const bool condition, const char* message) {
  3822. if (!condition) {
  3823. Log::log(YGNodeRef{nullptr}, YGLogLevelFatal, nullptr, "%s\n", message);
  3824. }
  3825. }
  3826. void YGAssertWithNode(
  3827. const YGNodeRef node,
  3828. const bool condition,
  3829. const char* message) {
  3830. if (!condition) {
  3831. Log::log(node, YGLogLevelFatal, nullptr, "%s\n", message);
  3832. }
  3833. }
  3834. void YGAssertWithConfig(
  3835. const YGConfigRef config,
  3836. const bool condition,
  3837. const char* message) {
  3838. if (!condition) {
  3839. Log::log(config, YGLogLevelFatal, nullptr, "%s\n", message);
  3840. }
  3841. }
  3842. void YGConfigSetExperimentalFeatureEnabled(
  3843. const YGConfigRef config,
  3844. const YGExperimentalFeature feature,
  3845. const bool enabled) {
  3846. config->experimentalFeatures[feature] = enabled;
  3847. }
  3848. inline bool YGConfigIsExperimentalFeatureEnabled(
  3849. const YGConfigRef config,
  3850. const YGExperimentalFeature feature) {
  3851. return config->experimentalFeatures[feature];
  3852. }
  3853. void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) {
  3854. config->useWebDefaults = enabled;
  3855. }
  3856. void YGConfigSetUseLegacyStretchBehaviour(
  3857. const YGConfigRef config,
  3858. const bool useLegacyStretchBehaviour) {
  3859. config->useLegacyStretchBehaviour = useLegacyStretchBehaviour;
  3860. }
  3861. bool YGConfigGetUseWebDefaults(const YGConfigRef config) {
  3862. return config->useWebDefaults;
  3863. }
  3864. void YGConfigSetContext(const YGConfigRef config, void* context) {
  3865. config->context = context;
  3866. }
  3867. void* YGConfigGetContext(const YGConfigRef config) {
  3868. return config->context;
  3869. }
  3870. void YGConfigSetCloneNodeFunc(
  3871. const YGConfigRef config,
  3872. const YGCloneNodeFunc callback) {
  3873. config->setCloneNodeCallback(callback);
  3874. }
  3875. static void YGTraverseChildrenPreOrder(
  3876. const YGVector& children,
  3877. const std::function<void(YGNodeRef node)>& f) {
  3878. for (YGNodeRef node : children) {
  3879. f(node);
  3880. YGTraverseChildrenPreOrder(node->getChildren(), f);
  3881. }
  3882. }
  3883. void YGTraversePreOrder(
  3884. YGNodeRef const node,
  3885. std::function<void(YGNodeRef node)>&& f) {
  3886. if (!node) {
  3887. return;
  3888. }
  3889. f(node);
  3890. YGTraverseChildrenPreOrder(node->getChildren(), f);
  3891. }