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

source code of qtdeclarative/src/3rdparty/yoga/Yoga.cpp