2 * Copyright (c) 2014-present, Facebook, Inc.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
14 #include "YGNodePrint.h"
15 #include "Yoga-internal.h"
19 /* define fmaxf if < VC12 */
21 __forceinline const float fmaxf(const float a, const float b) {
22 if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) {
23 return (a > b) ? a : b;
25 return YGFloatIsUndefined(a) ? b : a;
31 static int YGAndroidLog(const YGConfigRef config,
37 static int YGDefaultLog(const YGConfigRef config,
44 const YGValue YGValueZero = {0, YGUnitPoint};
45 const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined};
46 const YGValue YGValueAuto = {YGUndefined, YGUnitAuto};
49 #include <android/log.h>
50 static int YGAndroidLog(const YGConfigRef config,
55 int androidLevel = YGLogLevelDebug;
58 androidLevel = ANDROID_LOG_FATAL;
61 androidLevel = ANDROID_LOG_ERROR;
64 androidLevel = ANDROID_LOG_WARN;
67 androidLevel = ANDROID_LOG_INFO;
70 androidLevel = ANDROID_LOG_DEBUG;
72 case YGLogLevelVerbose:
73 androidLevel = ANDROID_LOG_VERBOSE;
76 const int result = __android_log_vprint(androidLevel, "yoga", format, args);
80 #define YG_UNUSED(x) (void)(x);
82 static int YGDefaultLog(const YGConfigRef config,
92 return vfprintf(stderr, format, args);
96 case YGLogLevelVerbose:
98 return vprintf(format, args);
105 bool YGFloatIsUndefined(const float value) {
106 // Value of a float in the case of it being not defined is 10.1E20. Earlier
107 // it used to be NAN, the benefit of which was that if NAN is involved in any
108 // mathematical expression the result was NAN. But since we want to have
109 // `-ffast-math` flag being used by compiler which assumes that the floating
110 // point values are not NAN and Inf, we represent YGUndefined as 10.1E20. But
111 // now if YGUndefined is involved in any mathematical operations this
112 // value(10.1E20) would change. So the following check makes sure that if the
113 // value is outside a range (-10E8, 10E8) then it is undefined.
114 return value >= 10E8 || value <= -10E8;
117 const YGValue* YGComputedEdgeValue(
118 const std::array<YGValue, YGEdgeCount>& edges,
120 const YGValue* const defaultValue) {
121 if (edges[edge].unit != YGUnitUndefined) {
125 if ((edge == YGEdgeTop || edge == YGEdgeBottom) &&
126 edges[YGEdgeVertical].unit != YGUnitUndefined) {
127 return &edges[YGEdgeVertical];
130 if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) &&
131 edges[YGEdgeHorizontal].unit != YGUnitUndefined) {
132 return &edges[YGEdgeHorizontal];
135 if (edges[YGEdgeAll].unit != YGUnitUndefined) {
136 return &edges[YGEdgeAll];
139 if (edge == YGEdgeStart || edge == YGEdgeEnd) {
140 return &YGValueUndefined;
146 void* YGNodeGetContext(YGNodeRef node) {
147 return node->getContext();
150 void YGNodeSetContext(YGNodeRef node, void* context) {
151 return node->setContext(context);
154 YGMeasureFunc YGNodeGetMeasureFunc(YGNodeRef node) {
155 return node->getMeasure();
158 void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
159 node->setMeasureFunc(measureFunc);
162 YGBaselineFunc YGNodeGetBaselineFunc(YGNodeRef node) {
163 return node->getBaseline();
166 void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) {
167 node->setBaseLineFunc(baselineFunc);
170 YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node) {
171 return node->getDirtied();
174 void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) {
175 node->setDirtiedFunc(dirtiedFunc);
178 YGPrintFunc YGNodeGetPrintFunc(YGNodeRef node) {
179 return node->getPrintFunc();
182 void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
183 node->setPrintFunc(printFunc);
186 bool YGNodeGetHasNewLayout(YGNodeRef node) {
187 return node->getHasNewLayout();
190 void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
191 node->setHasNewLayout(hasNewLayout);
194 YGNodeType YGNodeGetNodeType(YGNodeRef node) {
195 return node->getNodeType();
198 void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
199 return node->setNodeType(nodeType);
202 bool YGNodeIsDirty(YGNodeRef node) {
203 return node->isDirty();
206 bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) {
207 return node->didUseLegacyFlag();
210 void YGNodeMarkDirtyAndPropogateToDescendants(const YGNodeRef node) {
211 return node->markDirtyAndPropogateDownwards();
214 int32_t gNodeInstanceCount = 0;
215 int32_t gConfigInstanceCount = 0;
217 WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
218 const YGNodeRef node = new YGNode();
220 config, node != nullptr, "Could not allocate memory for node");
221 gNodeInstanceCount++;
223 if (config->useWebDefaults) {
224 node->setStyleFlexDirection(YGFlexDirectionRow);
225 node->setStyleAlignContent(YGAlignStretch);
227 node->setConfig(config);
231 YGConfigRef YGConfigGetDefault() {
232 static YGConfigRef defaultConfig = YGConfigNew();
233 return defaultConfig;
236 YGNodeRef YGNodeNew(void) {
237 return YGNodeNewWithConfig(YGConfigGetDefault());
240 YGNodeRef YGNodeClone(YGNodeRef oldNode) {
241 YGNodeRef node = new YGNode(*oldNode);
243 oldNode->getConfig(),
245 "Could not allocate memory for node");
246 gNodeInstanceCount++;
247 node->setOwner(nullptr);
251 static YGConfigRef YGConfigClone(const YGConfig& oldConfig) {
252 const YGConfigRef config = new YGConfig(oldConfig);
253 YGAssert(config != nullptr, "Could not allocate memory for config");
254 if (config == nullptr) {
257 gConfigInstanceCount++;
261 static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) {
262 YGNodeRef node = YGNodeClone(oldNode);
263 YGVector vec = YGVector();
264 vec.reserve(oldNode->getChildren().size());
265 YGNodeRef childNode = nullptr;
266 for (auto& item : oldNode->getChildren()) {
267 childNode = YGNodeDeepClone(item);
268 childNode->setOwner(node);
269 vec.push_back(childNode);
271 node->setChildren(vec);
273 if (oldNode->getConfig() != nullptr) {
274 node->setConfig(YGConfigClone(*(oldNode->getConfig())));
277 if (oldNode->getNextChild() != nullptr) {
278 node->setNextChild(YGNodeDeepClone(oldNode->getNextChild()));
284 void YGNodeFree(const YGNodeRef node) {
285 if (YGNodeRef owner = node->getOwner()) {
286 owner->removeChild(node);
287 node->setOwner(nullptr);
290 const uint32_t childCount = YGNodeGetChildCount(node);
291 for (uint32_t i = 0; i < childCount; i++) {
292 const YGNodeRef child = YGNodeGetChild(node, i);
293 child->setOwner(nullptr);
296 node->clearChildren();
298 gNodeInstanceCount--;
301 static void YGConfigFreeRecursive(const YGNodeRef root) {
302 if (root->getConfig() != nullptr) {
303 gConfigInstanceCount--;
304 delete root->getConfig();
306 // Delete configs recursively for childrens
307 for (uint32_t i = 0; i < root->getChildrenCount(); ++i) {
308 YGConfigFreeRecursive(root->getChild(i));
312 void YGNodeFreeRecursive(const YGNodeRef root) {
313 while (YGNodeGetChildCount(root) > 0) {
314 const YGNodeRef child = YGNodeGetChild(root, 0);
315 if (child->getOwner() != root) {
316 // Don't free shared nodes that we don't own.
319 YGNodeRemoveChild(root, child);
320 YGNodeFreeRecursive(child);
325 void YGNodeReset(const YGNodeRef node) {
326 YGAssertWithNode(node,
327 YGNodeGetChildCount(node) == 0,
328 "Cannot reset a node which still has children attached");
331 node->getOwner() == nullptr,
332 "Cannot reset a node still attached to a owner");
334 node->clearChildren();
336 const YGConfigRef config = node->getConfig();
338 if (config->useWebDefaults) {
339 node->setStyleFlexDirection(YGFlexDirectionRow);
340 node->setStyleAlignContent(YGAlignStretch);
342 node->setConfig(config);
345 int32_t YGNodeGetInstanceCount(void) {
346 return gNodeInstanceCount;
349 int32_t YGConfigGetInstanceCount(void) {
350 return gConfigInstanceCount;
353 YGConfigRef YGConfigNew(void) {
355 const YGConfigRef config = new YGConfig(YGAndroidLog);
357 const YGConfigRef config = new YGConfig(YGDefaultLog);
359 gConfigInstanceCount++;
363 void YGConfigFree(const YGConfigRef config) {
365 gConfigInstanceCount--;
368 void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) {
369 memcpy(dest, src, sizeof(YGConfig));
372 void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) {
375 child->getOwner() == nullptr,
376 "Child already has a owner, it must be removed first.");
380 node->getMeasure() == nullptr,
381 "Cannot add child: Nodes with measure functions cannot have children.");
383 node->cloneChildrenIfNeeded();
384 node->insertChild(child, index);
385 YGNodeRef owner = child->getOwner() ? nullptr : node;
386 child->setOwner(owner);
387 node->markDirtyAndPropogate();
390 void YGNodeInsertSharedChild(
391 const YGNodeRef node,
392 const YGNodeRef child,
393 const uint32_t index) {
396 node->getMeasure() == nullptr,
397 "Cannot add child: Nodes with measure functions cannot have children.");
399 node->insertChild(child, index);
400 child->setOwner(nullptr);
401 node->markDirtyAndPropogate();
404 void YGNodeRemoveChild(const YGNodeRef owner, const YGNodeRef excludedChild) {
405 // This algorithm is a forked variant from cloneChildrenIfNeeded in YGNode
406 // that excludes a child.
407 const uint32_t childCount = YGNodeGetChildCount(owner);
409 if (childCount == 0) {
410 // This is an empty set. Nothing to remove.
413 const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
414 if (firstChild->getOwner() == owner) {
415 // If the first child has this node as its owner, we assume that it is already unique.
416 // We can now try to delete a child in this list.
417 if (owner->removeChild(excludedChild)) {
418 excludedChild->setLayout(
419 YGNode().getLayout()); // layout is no longer valid
420 excludedChild->setOwner(nullptr);
421 owner->markDirtyAndPropogate();
425 // Otherwise we have to clone the node list except for the child we're trying to delete.
426 // We don't want to simply clone all children, because then the host will need to free
427 // the clone of the child that was just deleted.
428 const YGCloneNodeFunc cloneNodeCallback =
429 owner->getConfig()->cloneNodeCallback;
430 uint32_t nextInsertIndex = 0;
431 for (uint32_t i = 0; i < childCount; i++) {
432 const YGNodeRef oldChild = owner->getChild(i);
433 if (excludedChild == oldChild) {
434 // Ignore the deleted child. Don't reset its layout or owner since it is still valid
435 // in the other owner. However, since this owner has now changed, we need to mark it
437 owner->markDirtyAndPropogate();
440 YGNodeRef newChild = nullptr;
441 if (cloneNodeCallback) {
442 newChild = cloneNodeCallback(oldChild, owner, nextInsertIndex);
444 if (newChild == nullptr) {
445 newChild = YGNodeClone(oldChild);
447 owner->replaceChild(newChild, nextInsertIndex);
448 newChild->setOwner(owner);
452 while (nextInsertIndex < childCount) {
453 owner->removeChild(nextInsertIndex);
458 void YGNodeRemoveAllChildren(const YGNodeRef owner) {
459 const uint32_t childCount = YGNodeGetChildCount(owner);
460 if (childCount == 0) {
461 // This is an empty set already. Nothing to do.
464 const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
465 if (firstChild->getOwner() == owner) {
466 // If the first child has this node as its owner, we assume that this child set is unique.
467 for (uint32_t i = 0; i < childCount; i++) {
468 const YGNodeRef oldChild = YGNodeGetChild(owner, i);
469 oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid
470 oldChild->setOwner(nullptr);
472 owner->clearChildren();
473 owner->markDirtyAndPropogate();
476 // Otherwise, we are not the owner of the child set. We don't have to do anything to clear it.
477 owner->setChildren(YGVector());
478 owner->markDirtyAndPropogate();
481 static void YGNodeSetChildrenInternal(YGNodeRef const owner, const std::vector<YGNodeRef> &children)
486 if (children.size() == 0) {
487 if (YGNodeGetChildCount(owner) > 0) {
488 for (YGNodeRef const child : owner->getChildren()) {
489 child->setLayout(YGLayout());
490 child->setOwner(nullptr);
492 owner->setChildren(YGVector());
493 owner->markDirtyAndPropogate();
496 if (YGNodeGetChildCount(owner) > 0) {
497 for (YGNodeRef const oldChild : owner->getChildren()) {
498 // Our new children may have nodes in common with the old children. We don't reset these common nodes.
499 if (std::find(children.begin(), children.end(), oldChild) == children.end()) {
500 oldChild->setLayout(YGLayout());
501 oldChild->setOwner(nullptr);
505 owner->setChildren(children);
506 for (YGNodeRef child : children) {
507 child->setOwner(owner);
509 owner->markDirtyAndPropogate();
513 void YGNodeSetChildren(YGNodeRef const owner, const YGNodeRef c[], const uint32_t count) {
514 const YGVector children = {c, c + count};
515 YGNodeSetChildrenInternal(owner, children);
518 void YGNodeSetChildren(YGNodeRef const owner, const std::vector<YGNodeRef> &children)
520 YGNodeSetChildrenInternal(owner, children);
523 YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) {
524 if (index < node->getChildren().size()) {
525 return node->getChild(index);
530 uint32_t YGNodeGetChildCount(const YGNodeRef node) {
531 return static_cast<uint32_t>(node->getChildren().size());
534 YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
535 return node->getOwner();
538 void YGNodeMarkDirty(const YGNodeRef node) {
541 node->getMeasure() != nullptr,
542 "Only leaf nodes with custom measure functions"
543 "should manually mark themselves as dirty");
545 node->markDirtyAndPropogate();
548 void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) {
549 if (!(dstNode->getStyle() == srcNode->getStyle())) {
550 dstNode->setStyle(srcNode->getStyle());
551 dstNode->markDirtyAndPropogate();
555 float YGNodeStyleGetFlexGrow(const YGNodeRef node) {
556 return node->getStyle().flexGrow.isUndefined()
558 : node->getStyle().flexGrow.getValue();
561 float YGNodeStyleGetFlexShrink(const YGNodeRef node) {
562 return node->getStyle().flexShrink.isUndefined()
563 ? (node->getConfig()->useWebDefaults ? kWebDefaultFlexShrink
564 : kDefaultFlexShrink)
565 : node->getStyle().flexShrink.getValue();
568 #define YG_NODE_STYLE_PROPERTY_SETTER_IMPL( \
569 type, name, paramName, instanceName) \
570 void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
571 if (node->getStyle().instanceName != paramName) { \
572 YGStyle style = node->getStyle(); \
573 style.instanceName = paramName; \
574 node->setStyle(style); \
575 node->markDirtyAndPropogate(); \
579 #define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL( \
580 type, name, paramName, instanceName) \
581 void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
583 YGFloatSanitize(paramName), \
584 YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \
586 if ((node->getStyle().instanceName.value != value.value && \
587 value.unit != YGUnitUndefined) || \
588 node->getStyle().instanceName.unit != value.unit) { \
589 YGStyle style = node->getStyle(); \
590 style.instanceName = value; \
591 node->setStyle(style); \
592 node->markDirtyAndPropogate(); \
596 void YGNodeStyleSet##name##Percent( \
597 const YGNodeRef node, const type paramName) { \
599 YGFloatSanitize(paramName), \
600 YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \
602 if ((node->getStyle().instanceName.value != value.value && \
603 value.unit != YGUnitUndefined) || \
604 node->getStyle().instanceName.unit != value.unit) { \
605 YGStyle style = node->getStyle(); \
607 style.instanceName = value; \
608 node->setStyle(style); \
609 node->markDirtyAndPropogate(); \
613 #define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \
614 type, name, paramName, instanceName) \
615 void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
617 YGFloatSanitize(paramName), \
618 YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \
620 if ((node->getStyle().instanceName.value != value.value && \
621 value.unit != YGUnitUndefined) || \
622 node->getStyle().instanceName.unit != value.unit) { \
623 YGStyle style = node->getStyle(); \
624 style.instanceName = value; \
625 node->setStyle(style); \
626 node->markDirtyAndPropogate(); \
630 void YGNodeStyleSet##name##Percent( \
631 const YGNodeRef node, const type paramName) { \
632 if (node->getStyle().instanceName.value != YGFloatSanitize(paramName) || \
633 node->getStyle().instanceName.unit != YGUnitPercent) { \
634 YGStyle style = node->getStyle(); \
635 style.instanceName.value = YGFloatSanitize(paramName); \
636 style.instanceName.unit = \
637 YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent; \
638 node->setStyle(style); \
639 node->markDirtyAndPropogate(); \
643 void YGNodeStyleSet##name##Auto(const YGNodeRef node) { \
644 if (node->getStyle().instanceName.unit != YGUnitAuto) { \
645 YGStyle style = node->getStyle(); \
646 style.instanceName.value = 0; \
647 style.instanceName.unit = YGUnitAuto; \
648 node->setStyle(style); \
649 node->markDirtyAndPropogate(); \
653 #define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName) \
654 YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \
656 type YGNodeStyleGet##name(const YGNodeRef node) { \
657 return node->getStyle().instanceName; \
660 #define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \
661 YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL( \
662 float, name, paramName, instanceName) \
664 type YGNodeStyleGet##name(const YGNodeRef node) { \
665 YGValue value = node->getStyle().instanceName; \
666 if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
667 value.value = YGUndefined; \
672 #define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL( \
673 type, name, paramName, instanceName) \
674 YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL( \
675 float, name, paramName, instanceName) \
677 type YGNodeStyleGet##name(const YGNodeRef node) { \
678 YGValue value = node->getStyle().instanceName; \
679 if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
680 value.value = YGUndefined; \
685 #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \
686 void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \
687 if (node->getStyle().instanceName[edge].unit != YGUnitAuto) { \
688 YGStyle style = node->getStyle(); \
689 style.instanceName[edge].value = 0; \
690 style.instanceName[edge].unit = YGUnitAuto; \
691 node->setStyle(style); \
692 node->markDirtyAndPropogate(); \
696 #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL( \
697 type, name, paramName, instanceName) \
698 void YGNodeStyleSet##name( \
699 const YGNodeRef node, const YGEdge edge, const float paramName) { \
701 YGFloatSanitize(paramName), \
702 YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint, \
704 if ((node->getStyle().instanceName[edge].value != value.value && \
705 value.unit != YGUnitUndefined) || \
706 node->getStyle().instanceName[edge].unit != value.unit) { \
707 YGStyle style = node->getStyle(); \
708 style.instanceName[edge] = value; \
709 node->setStyle(style); \
710 node->markDirtyAndPropogate(); \
714 void YGNodeStyleSet##name##Percent( \
715 const YGNodeRef node, const YGEdge edge, const float paramName) { \
717 YGFloatSanitize(paramName), \
718 YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \
720 if ((node->getStyle().instanceName[edge].value != value.value && \
721 value.unit != YGUnitUndefined) || \
722 node->getStyle().instanceName[edge].unit != value.unit) { \
723 YGStyle style = node->getStyle(); \
724 style.instanceName[edge] = value; \
725 node->setStyle(style); \
726 node->markDirtyAndPropogate(); \
731 YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) { \
732 YGValue value = node->getStyle().instanceName[edge]; \
733 if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
734 value.value = YGUndefined; \
736 return WIN_STRUCT_REF(value); \
739 #define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \
740 type YGNodeLayoutGet##name(const YGNodeRef node) { \
741 return node->getLayout().instanceName; \
744 #define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \
745 type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \
749 "Cannot get layout properties of multi-edge shorthands"); \
751 if (edge == YGEdgeLeft) { \
752 if (node->getLayout().direction == YGDirectionRTL) { \
753 return node->getLayout().instanceName[YGEdgeEnd]; \
755 return node->getLayout().instanceName[YGEdgeStart]; \
759 if (edge == YGEdgeRight) { \
760 if (node->getLayout().direction == YGDirectionRTL) { \
761 return node->getLayout().instanceName[YGEdgeStart]; \
763 return node->getLayout().instanceName[YGEdgeEnd]; \
767 return node->getLayout().instanceName[edge]; \
770 // YG_NODE_PROPERTY_IMPL(void *, Context, context, context);
771 // YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print);
772 // YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout);
773 // YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType);
775 YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction);
776 YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection);
777 YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent);
778 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent);
779 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems);
780 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf);
781 YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType);
782 YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap);
783 YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow);
784 YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display);
786 // TODO(T26792433): Change the API to accept YGFloatOptional.
787 void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) {
788 if (node->getStyle().flex != flex) {
789 YGStyle style = node->getStyle();
790 if (YGFloatIsUndefined(flex)) {
791 style.flex = YGFloatOptional();
793 style.flex = YGFloatOptional(flex);
795 node->setStyle(style);
796 node->markDirtyAndPropogate();
800 // TODO(T26792433): Change the API to accept YGFloatOptional.
801 float YGNodeStyleGetFlex(const YGNodeRef node) {
802 return node->getStyle().flex.isUndefined() ? YGUndefined
803 : node->getStyle().flex.getValue();
806 // TODO(T26792433): Change the API to accept YGFloatOptional.
807 void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) {
808 if (node->getStyle().flexGrow != flexGrow) {
809 YGStyle style = node->getStyle();
810 if (YGFloatIsUndefined(flexGrow)) {
811 style.flexGrow = YGFloatOptional();
813 style.flexGrow = YGFloatOptional(flexGrow);
815 node->setStyle(style);
816 node->markDirtyAndPropogate();
820 // TODO(T26792433): Change the API to accept YGFloatOptional.
821 void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) {
822 if (node->getStyle().flexShrink != flexShrink) {
823 YGStyle style = node->getStyle();
824 if (YGFloatIsUndefined(flexShrink)) {
825 style.flexShrink = YGFloatOptional();
827 style.flexShrink = YGFloatOptional(flexShrink);
829 node->setStyle(style);
830 node->markDirtyAndPropogate();
834 YGValue YGNodeStyleGetFlexBasis(const YGNodeRef node) {
835 YGValue flexBasis = node->getStyle().flexBasis;
836 if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) {
837 // TODO(T26792433): Get rid off the use of YGUndefined at client side
838 flexBasis.value = YGUndefined;
843 void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) {
845 YGFloatSanitize(flexBasis),
846 YGFloatIsUndefined(flexBasis) ? YGUnitUndefined : YGUnitPoint,
848 if ((node->getStyle().flexBasis.value != value.value &&
849 value.unit != YGUnitUndefined) ||
850 node->getStyle().flexBasis.unit != value.unit) {
851 YGStyle style = node->getStyle();
852 style.flexBasis = value;
853 node->setStyle(style);
854 node->markDirtyAndPropogate();
858 void YGNodeStyleSetFlexBasisPercent(
859 const YGNodeRef node,
860 const float flexBasisPercent) {
861 if (node->getStyle().flexBasis.value != flexBasisPercent ||
862 node->getStyle().flexBasis.unit != YGUnitPercent) {
863 YGStyle style = node->getStyle();
864 style.flexBasis.value = YGFloatSanitize(flexBasisPercent);
865 style.flexBasis.unit =
866 YGFloatIsUndefined(flexBasisPercent) ? YGUnitAuto : YGUnitPercent;
867 node->setStyle(style);
868 node->markDirtyAndPropogate();
872 void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) {
873 if (node->getStyle().flexBasis.unit != YGUnitAuto) {
874 YGStyle style = node->getStyle();
875 style.flexBasis.value = 0;
876 style.flexBasis.unit = YGUnitAuto;
877 node->setStyle(style);
878 node->markDirtyAndPropogate();
882 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position);
883 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin);
884 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin);
885 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding);
887 // TODO(T26792433): Change the API to accept YGFloatOptional.
888 void YGNodeStyleSetBorder(
889 const YGNodeRef node,
891 const float border) {
893 YGFloatSanitize(border),
894 YGFloatIsUndefined(border) ? YGUnitUndefined : YGUnitPoint,
896 if ((node->getStyle().border[edge].value != value.value &&
897 value.unit != YGUnitUndefined) ||
898 node->getStyle().border[edge].unit != value.unit) {
899 YGStyle style = node->getStyle();
900 style.border[edge] = value;
901 node->setStyle(style);
902 node->markDirtyAndPropogate();
906 float YGNodeStyleGetBorder(const YGNodeRef node, const YGEdge edge) {
907 if (node->getStyle().border[edge].unit == YGUnitUndefined ||
908 node->getStyle().border[edge].unit == YGUnitAuto) {
909 // TODO(T26792433): Rather than returning YGUndefined, change the api to
910 // return YGFloatOptional.
914 return node->getStyle().border[edge].value;
917 // Yoga specific properties, not compatible with flexbox specification
919 // TODO(T26792433): Change the API to accept YGFloatOptional.
920 float YGNodeStyleGetAspectRatio(const YGNodeRef node) {
921 const YGFloatOptional op = node->getStyle().aspectRatio;
922 return op.isUndefined() ? YGUndefined : op.getValue();
925 // TODO(T26792433): Change the API to accept YGFloatOptional.
926 void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) {
927 if (node->getStyle().aspectRatio != aspectRatio) {
928 YGStyle style = node->getStyle();
929 style.aspectRatio = YGFloatOptional(aspectRatio);
930 node->setStyle(style);
931 node->markDirtyAndPropogate();
935 YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]);
936 YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]);
937 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]);
938 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]);
939 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]);
940 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]);
941 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]);
942 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]);
943 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]);
944 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]);
945 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]);
946 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]);
947 YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction);
948 YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow);
950 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin);
951 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border);
952 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding);
954 bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(const YGNodeRef node) {
955 return node->getLayout().doesLegacyStretchFlagAffectsLayout;
958 uint32_t gCurrentGenerationCount = 0;
960 bool YGLayoutNodeInternal(const YGNodeRef node,
961 const float availableWidth,
962 const float availableHeight,
963 const YGDirection ownerDirection,
964 const YGMeasureMode widthMeasureMode,
965 const YGMeasureMode heightMeasureMode,
966 const float ownerWidth,
967 const float ownerHeight,
968 const bool performLayout,
970 const YGConfigRef config);
972 static void YGNodePrintInternal(const YGNodeRef node,
973 const YGPrintOptions options) {
975 facebook::yoga::YGNodeToString(&str, node, options, 0);
976 YGLog(node, YGLogLevelDebug, str.c_str());
979 void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) {
980 YGNodePrintInternal(node, options);
983 const std::array<YGEdge, 4> leading = {
984 {YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}};
986 const std::array<YGEdge, 4> trailing = {
987 {YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}};
988 static const std::array<YGEdge, 4> pos = {{
995 static const std::array<YGDimension, 4> dim = {
996 {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}};
998 static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node,
999 const YGFlexDirection axis,
1000 const float widthSize) {
1001 return YGUnwrapFloatOptional(
1002 node->getLeadingPaddingAndBorder(axis, widthSize) +
1003 node->getTrailingPaddingAndBorder(axis, widthSize));
1006 static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) {
1007 const YGAlign align = child->getStyle().alignSelf == YGAlignAuto
1008 ? node->getStyle().alignItems
1009 : child->getStyle().alignSelf;
1010 if (align == YGAlignBaseline &&
1011 YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
1012 return YGAlignFlexStart;
1017 static float YGBaseline(const YGNodeRef node) {
1018 if (node->getBaseline() != nullptr) {
1019 const float baseline = node->getBaseline()(
1021 node->getLayout().measuredDimensions[YGDimensionWidth],
1022 node->getLayout().measuredDimensions[YGDimensionHeight]);
1023 YGAssertWithNode(node,
1024 !YGFloatIsUndefined(baseline),
1025 "Expect custom baseline function to not return NaN");
1029 YGNodeRef baselineChild = nullptr;
1030 const uint32_t childCount = YGNodeGetChildCount(node);
1031 for (uint32_t i = 0; i < childCount; i++) {
1032 const YGNodeRef child = YGNodeGetChild(node, i);
1033 if (child->getLineIndex() > 0) {
1036 if (child->getStyle().positionType == YGPositionTypeAbsolute) {
1039 if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
1040 baselineChild = child;
1044 if (baselineChild == nullptr) {
1045 baselineChild = child;
1049 if (baselineChild == nullptr) {
1050 return node->getLayout().measuredDimensions[YGDimensionHeight];
1053 const float baseline = YGBaseline(baselineChild);
1054 return baseline + baselineChild->getLayout().position[YGEdgeTop];
1057 static bool YGIsBaselineLayout(const YGNodeRef node) {
1058 if (YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
1061 if (node->getStyle().alignItems == YGAlignBaseline) {
1064 const uint32_t childCount = YGNodeGetChildCount(node);
1065 for (uint32_t i = 0; i < childCount; i++) {
1066 const YGNodeRef child = YGNodeGetChild(node, i);
1067 if (child->getStyle().positionType == YGPositionTypeRelative &&
1068 child->getStyle().alignSelf == YGAlignBaseline) {
1076 static inline float YGNodeDimWithMargin(const YGNodeRef node,
1077 const YGFlexDirection axis,
1078 const float widthSize) {
1079 return node->getLayout().measuredDimensions[dim[axis]] +
1080 YGUnwrapFloatOptional(
1081 node->getLeadingMargin(axis, widthSize) +
1082 node->getTrailingMargin(axis, widthSize));
1085 static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node,
1086 const YGFlexDirection axis,
1087 const float ownerSize) {
1089 YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value);
1091 node->getResolvedDimension(dim[axis]).unit == YGUnitAuto ||
1092 node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined ||
1093 (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint &&
1094 !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) ||
1095 (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent &&
1097 (node->getResolvedDimension(dim[axis]).value < 0.0f ||
1098 YGFloatIsUndefined(ownerSize))));
1101 static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) {
1102 const float value = node->getLayout().measuredDimensions[dim[axis]];
1103 return !YGFloatIsUndefined(value) && value >= 0.0f;
1106 static YGFloatOptional YGNodeBoundAxisWithinMinAndMax(
1107 const YGNodeRef node,
1108 const YGFlexDirection& axis,
1110 const float& axisSize) {
1111 YGFloatOptional min;
1112 YGFloatOptional max;
1114 if (YGFlexDirectionIsColumn(axis)) {
1115 min = YGResolveValue(
1116 node->getStyle().minDimensions[YGDimensionHeight], axisSize);
1117 max = YGResolveValue(
1118 node->getStyle().maxDimensions[YGDimensionHeight], axisSize);
1119 } else if (YGFlexDirectionIsRow(axis)) {
1120 min = YGResolveValue(
1121 node->getStyle().minDimensions[YGDimensionWidth], axisSize);
1122 max = YGResolveValue(
1123 node->getStyle().maxDimensions[YGDimensionWidth], axisSize);
1126 if (!max.isUndefined() && max.getValue() >= 0 && value > max.getValue()) {
1130 if (!min.isUndefined() && min.getValue() >= 0 && value < min.getValue()) {
1134 return YGFloatOptional(value);
1137 // Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go
1139 // padding and border amount.
1140 static inline float YGNodeBoundAxis(const YGNodeRef node,
1141 const YGFlexDirection axis,
1143 const float axisSize,
1144 const float widthSize) {
1146 YGUnwrapFloatOptional(
1147 YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize)),
1148 YGNodePaddingAndBorderForAxis(node, axis, widthSize));
1151 static void YGNodeSetChildTrailingPosition(const YGNodeRef node,
1152 const YGNodeRef child,
1153 const YGFlexDirection axis) {
1154 const float size = child->getLayout().measuredDimensions[dim[axis]];
1155 child->setLayoutPosition(
1156 node->getLayout().measuredDimensions[dim[axis]] - size -
1157 child->getLayout().position[pos[axis]],
1161 static void YGConstrainMaxSizeForMode(const YGNodeRef node,
1162 const enum YGFlexDirection axis,
1163 const float ownerAxisSize,
1164 const float ownerWidth,
1165 YGMeasureMode *mode,
1167 const YGFloatOptional maxSize =
1169 node->getStyle().maxDimensions[dim[axis]], ownerAxisSize) +
1170 YGFloatOptional(node->getMarginForAxis(axis, ownerWidth));
1172 case YGMeasureModeExactly:
1173 case YGMeasureModeAtMost:
1174 *size = (maxSize.isUndefined() || *size < maxSize.getValue())
1176 : maxSize.getValue();
1178 case YGMeasureModeUndefined:
1179 if (!maxSize.isUndefined()) {
1180 *mode = YGMeasureModeAtMost;
1181 *size = maxSize.getValue();
1187 static void YGNodeComputeFlexBasisForChild(const YGNodeRef node,
1188 const YGNodeRef child,
1190 const YGMeasureMode widthMode,
1192 const float ownerWidth,
1193 const float ownerHeight,
1194 const YGMeasureMode heightMode,
1195 const YGDirection direction,
1196 const YGConfigRef config) {
1197 const YGFlexDirection mainAxis =
1198 YGResolveFlexDirection(node->getStyle().flexDirection, direction);
1199 const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1200 const float mainAxisSize = isMainAxisRow ? width : height;
1201 const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
1205 YGMeasureMode childWidthMeasureMode;
1206 YGMeasureMode childHeightMeasureMode;
1208 const YGFloatOptional resolvedFlexBasis =
1209 YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize);
1210 const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth);
1211 const bool isColumnStyleDimDefined =
1212 YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, ownerHeight);
1214 if (!resolvedFlexBasis.isUndefined() && !YGFloatIsUndefined(mainAxisSize)) {
1215 if (child->getLayout().computedFlexBasis.isUndefined() ||
1216 (YGConfigIsExperimentalFeatureEnabled(
1217 child->getConfig(), YGExperimentalFeatureWebFlexBasis) &&
1218 child->getLayout().computedFlexBasisGeneration !=
1219 gCurrentGenerationCount)) {
1220 const YGFloatOptional& paddingAndBorder = YGFloatOptional(
1221 YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth));
1222 child->setLayoutComputedFlexBasis(
1223 YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder));
1225 } else if (isMainAxisRow && isRowStyleDimDefined) {
1226 // The width is definite, so use that as the flex basis.
1227 const YGFloatOptional& paddingAndBorder = YGFloatOptional(
1228 YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth));
1230 child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
1232 child->getResolvedDimension(YGDimensionWidth), ownerWidth),
1234 } else if (!isMainAxisRow && isColumnStyleDimDefined) {
1235 // The height is definite, so use that as the flex basis.
1236 const YGFloatOptional& paddingAndBorder =
1237 YGFloatOptional(YGNodePaddingAndBorderForAxis(
1238 child, YGFlexDirectionColumn, ownerWidth));
1239 child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
1241 child->getResolvedDimension(YGDimensionHeight), ownerHeight),
1244 // Compute the flex basis and hypothetical main size (i.e. the clamped
1246 childWidth = YGUndefined;
1247 childHeight = YGUndefined;
1248 childWidthMeasureMode = YGMeasureModeUndefined;
1249 childHeightMeasureMode = YGMeasureModeUndefined;
1251 const float& marginRow = YGUnwrapFloatOptional(
1252 child->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1253 const float& marginColumn = YGUnwrapFloatOptional(
1254 child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1256 if (isRowStyleDimDefined) {
1258 YGUnwrapFloatOptional(YGResolveValue(
1259 child->getResolvedDimension(YGDimensionWidth), ownerWidth)) +
1261 childWidthMeasureMode = YGMeasureModeExactly;
1263 if (isColumnStyleDimDefined) {
1265 YGUnwrapFloatOptional(YGResolveValue(
1266 child->getResolvedDimension(YGDimensionHeight), ownerHeight)) +
1268 childHeightMeasureMode = YGMeasureModeExactly;
1271 // The W3C spec doesn't say anything about the 'overflow' property,
1272 // but all major browsers appear to implement the following logic.
1273 if ((!isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
1274 node->getStyle().overflow != YGOverflowScroll) {
1275 if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) {
1277 childWidthMeasureMode = YGMeasureModeAtMost;
1281 if ((isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
1282 node->getStyle().overflow != YGOverflowScroll) {
1283 if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) {
1284 childHeight = height;
1285 childHeightMeasureMode = YGMeasureModeAtMost;
1289 if (!child->getStyle().aspectRatio.isUndefined()) {
1290 if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) {
1291 childHeight = marginColumn +
1292 (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1293 childHeightMeasureMode = YGMeasureModeExactly;
1294 } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) {
1295 childWidth = marginRow +
1296 (childHeight - marginColumn) *
1297 child->getStyle().aspectRatio.getValue();
1298 childWidthMeasureMode = YGMeasureModeExactly;
1302 // If child has no defined size in the cross axis and is set to stretch,
1304 // axis to be measured exactly with the available inner width
1306 const bool hasExactWidth = !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly;
1307 const bool childWidthStretch = YGNodeAlignItem(node, child) == YGAlignStretch &&
1308 childWidthMeasureMode != YGMeasureModeExactly;
1309 if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && childWidthStretch) {
1311 childWidthMeasureMode = YGMeasureModeExactly;
1312 if (!child->getStyle().aspectRatio.isUndefined()) {
1314 (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1315 childHeightMeasureMode = YGMeasureModeExactly;
1319 const bool hasExactHeight = !YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly;
1320 const bool childHeightStretch = YGNodeAlignItem(node, child) == YGAlignStretch &&
1321 childHeightMeasureMode != YGMeasureModeExactly;
1322 if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && childHeightStretch) {
1323 childHeight = height;
1324 childHeightMeasureMode = YGMeasureModeExactly;
1326 if (!child->getStyle().aspectRatio.isUndefined()) {
1327 childWidth = (childHeight - marginColumn) *
1328 child->getStyle().aspectRatio.getValue();
1329 childWidthMeasureMode = YGMeasureModeExactly;
1333 YGConstrainMaxSizeForMode(
1334 child, YGFlexDirectionRow, ownerWidth, ownerWidth, &childWidthMeasureMode, &childWidth);
1335 YGConstrainMaxSizeForMode(child,
1336 YGFlexDirectionColumn,
1339 &childHeightMeasureMode,
1342 // Measure the child
1343 YGLayoutNodeInternal(child,
1347 childWidthMeasureMode,
1348 childHeightMeasureMode,
1355 child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax(
1356 child->getLayout().measuredDimensions[dim[mainAxis]],
1357 YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth))));
1359 child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
1362 static void YGNodeAbsoluteLayoutChild(const YGNodeRef node,
1363 const YGNodeRef child,
1365 const YGMeasureMode widthMode,
1367 const YGDirection direction,
1368 const YGConfigRef config) {
1369 const YGFlexDirection mainAxis =
1370 YGResolveFlexDirection(node->getStyle().flexDirection, direction);
1371 const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
1372 const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1374 float childWidth = YGUndefined;
1375 float childHeight = YGUndefined;
1376 YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined;
1377 YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined;
1379 const float& marginRow =
1380 YGUnwrapFloatOptional(child->getMarginForAxis(YGFlexDirectionRow, width));
1381 const float& marginColumn = YGUnwrapFloatOptional(
1382 child->getMarginForAxis(YGFlexDirectionColumn, width));
1384 if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) {
1386 YGUnwrapFloatOptional(YGResolveValue(child->getResolvedDimension(YGDimensionWidth), width)) +
1389 // If the child doesn't have a specified width, compute the width based
1390 // on the left/right
1391 // offsets if they're defined.
1392 if (child->isLeadingPositionDefined(YGFlexDirectionRow) &&
1393 child->isTrailingPosDefined(YGFlexDirectionRow)) {
1394 childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] -
1395 (node->getLeadingBorder(YGFlexDirectionRow) +
1396 node->getTrailingBorder(YGFlexDirectionRow)) -
1397 YGUnwrapFloatOptional(
1398 child->getLeadingPosition(YGFlexDirectionRow, width) +
1399 child->getTrailingPosition(YGFlexDirectionRow, width));
1400 childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width);
1404 if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) {
1406 YGUnwrapFloatOptional(YGResolveValue(child->getResolvedDimension(YGDimensionHeight), height)) +
1409 // If the child doesn't have a specified height, compute the height
1410 // based on the top/bottom
1411 // offsets if they're defined.
1412 if (child->isLeadingPositionDefined(YGFlexDirectionColumn) &&
1413 child->isTrailingPosDefined(YGFlexDirectionColumn)) {
1415 node->getLayout().measuredDimensions[YGDimensionHeight] -
1416 (node->getLeadingBorder(YGFlexDirectionColumn) +
1417 node->getTrailingBorder(YGFlexDirectionColumn)) -
1418 YGUnwrapFloatOptional(
1419 child->getLeadingPosition(YGFlexDirectionColumn, height) +
1420 child->getTrailingPosition(YGFlexDirectionColumn, height));
1421 childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width);
1425 // Exactly one dimension needs to be defined for us to be able to do aspect ratio
1426 // calculation. One dimension being the anchor and the other being flexible.
1427 if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) {
1428 if (!child->getStyle().aspectRatio.isUndefined()) {
1429 if (YGFloatIsUndefined(childWidth)) {
1430 childWidth = marginRow +
1431 (childHeight - marginColumn) *
1432 child->getStyle().aspectRatio.getValue();
1433 } else if (YGFloatIsUndefined(childHeight)) {
1434 childHeight = marginColumn +
1435 (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1440 // If we're still missing one or the other dimension, measure the content.
1441 if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) {
1442 childWidthMeasureMode =
1443 YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly;
1444 childHeightMeasureMode =
1445 YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly;
1447 // If the size of the owner is defined then try to constrain the absolute child to that size
1448 // as well. This allows text within the absolute child to wrap to the size of its owner.
1449 // This is the same behavior as many browsers implement.
1450 if (!isMainAxisRow && YGFloatIsUndefined(childWidth) &&
1451 widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) &&
1454 childWidthMeasureMode = YGMeasureModeAtMost;
1457 YGLayoutNodeInternal(child,
1461 childWidthMeasureMode,
1462 childHeightMeasureMode,
1468 childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] +
1469 YGUnwrapFloatOptional(
1470 child->getMarginForAxis(YGFlexDirectionRow, width));
1471 childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] +
1472 YGUnwrapFloatOptional(
1473 child->getMarginForAxis(YGFlexDirectionColumn, width));
1476 YGLayoutNodeInternal(child,
1480 YGMeasureModeExactly,
1481 YGMeasureModeExactly,
1488 if (child->isTrailingPosDefined(mainAxis) &&
1489 !child->isLeadingPositionDefined(mainAxis)) {
1490 child->setLayoutPosition(
1491 node->getLayout().measuredDimensions[dim[mainAxis]] -
1492 child->getLayout().measuredDimensions[dim[mainAxis]] -
1493 node->getTrailingBorder(mainAxis) -
1494 YGUnwrapFloatOptional(child->getTrailingMargin(mainAxis, width)) -
1495 YGUnwrapFloatOptional(child->getTrailingPosition(
1496 mainAxis, isMainAxisRow ? width : height)),
1499 !child->isLeadingPositionDefined(mainAxis) &&
1500 node->getStyle().justifyContent == YGJustifyCenter) {
1501 child->setLayoutPosition(
1502 (node->getLayout().measuredDimensions[dim[mainAxis]] -
1503 child->getLayout().measuredDimensions[dim[mainAxis]]) /
1507 !child->isLeadingPositionDefined(mainAxis) &&
1508 node->getStyle().justifyContent == YGJustifyFlexEnd) {
1509 child->setLayoutPosition(
1510 (node->getLayout().measuredDimensions[dim[mainAxis]] -
1511 child->getLayout().measuredDimensions[dim[mainAxis]]),
1515 if (child->isTrailingPosDefined(crossAxis) &&
1516 !child->isLeadingPositionDefined(crossAxis)) {
1517 child->setLayoutPosition(
1518 node->getLayout().measuredDimensions[dim[crossAxis]] -
1519 child->getLayout().measuredDimensions[dim[crossAxis]] -
1520 node->getTrailingBorder(crossAxis) -
1521 YGUnwrapFloatOptional(child->getTrailingMargin(crossAxis, width)) -
1522 YGUnwrapFloatOptional(child->getTrailingPosition(
1523 crossAxis, isMainAxisRow ? height : width)),
1524 leading[crossAxis]);
1527 !child->isLeadingPositionDefined(crossAxis) &&
1528 YGNodeAlignItem(node, child) == YGAlignCenter) {
1529 child->setLayoutPosition(
1530 (node->getLayout().measuredDimensions[dim[crossAxis]] -
1531 child->getLayout().measuredDimensions[dim[crossAxis]]) /
1533 leading[crossAxis]);
1535 !child->isLeadingPositionDefined(crossAxis) &&
1536 ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^
1537 (node->getStyle().flexWrap == YGWrapWrapReverse))) {
1538 child->setLayoutPosition(
1539 (node->getLayout().measuredDimensions[dim[crossAxis]] -
1540 child->getLayout().measuredDimensions[dim[crossAxis]]),
1541 leading[crossAxis]);
1545 static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node,
1546 const float availableWidth,
1547 const float availableHeight,
1548 const YGMeasureMode widthMeasureMode,
1549 const YGMeasureMode heightMeasureMode,
1550 const float ownerWidth,
1551 const float ownerHeight) {
1554 node->getMeasure() != nullptr,
1555 "Expected node to have custom measure function");
1557 const float paddingAndBorderAxisRow =
1558 YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth);
1559 const float paddingAndBorderAxisColumn =
1560 YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth);
1561 const float marginAxisRow = YGUnwrapFloatOptional(
1562 node->getMarginForAxis(YGFlexDirectionRow, availableWidth));
1563 const float marginAxisColumn = YGUnwrapFloatOptional(
1564 node->getMarginForAxis(YGFlexDirectionColumn, availableWidth));
1566 // We want to make sure we don't call measure with negative size
1567 const float innerWidth = YGFloatIsUndefined(availableWidth)
1569 : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow);
1570 const float innerHeight = YGFloatIsUndefined(availableHeight)
1573 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn);
1575 if (widthMeasureMode == YGMeasureModeExactly &&
1576 heightMeasureMode == YGMeasureModeExactly) {
1577 // Don't bother sizing the text if both dimensions are already defined.
1578 node->setLayoutMeasuredDimension(
1582 availableWidth - marginAxisRow,
1586 node->setLayoutMeasuredDimension(
1589 YGFlexDirectionColumn,
1590 availableHeight - marginAxisColumn,
1595 // Measure the text under the current constraints.
1596 const YGSize measuredSize = node->getMeasure()(
1597 node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode);
1599 node->setLayoutMeasuredDimension(
1603 (widthMeasureMode == YGMeasureModeUndefined ||
1604 widthMeasureMode == YGMeasureModeAtMost)
1605 ? measuredSize.width + paddingAndBorderAxisRow
1606 : availableWidth - marginAxisRow,
1611 node->setLayoutMeasuredDimension(
1614 YGFlexDirectionColumn,
1615 (heightMeasureMode == YGMeasureModeUndefined ||
1616 heightMeasureMode == YGMeasureModeAtMost)
1617 ? measuredSize.height + paddingAndBorderAxisColumn
1618 : availableHeight - marginAxisColumn,
1625 // For nodes with no children, use the available values if they were provided,
1626 // or the minimum size as indicated by the padding and border sizes.
1627 static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node,
1628 const float availableWidth,
1629 const float availableHeight,
1630 const YGMeasureMode widthMeasureMode,
1631 const YGMeasureMode heightMeasureMode,
1632 const float ownerWidth,
1633 const float ownerHeight) {
1634 const float paddingAndBorderAxisRow =
1635 YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth);
1636 const float paddingAndBorderAxisColumn =
1637 YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth);
1638 const float marginAxisRow = YGUnwrapFloatOptional(
1639 node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1640 const float marginAxisColumn = YGUnwrapFloatOptional(
1641 node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1643 node->setLayoutMeasuredDimension(
1647 (widthMeasureMode == YGMeasureModeUndefined ||
1648 widthMeasureMode == YGMeasureModeAtMost)
1649 ? paddingAndBorderAxisRow
1650 : availableWidth - marginAxisRow,
1655 node->setLayoutMeasuredDimension(
1658 YGFlexDirectionColumn,
1659 (heightMeasureMode == YGMeasureModeUndefined ||
1660 heightMeasureMode == YGMeasureModeAtMost)
1661 ? paddingAndBorderAxisColumn
1662 : availableHeight - marginAxisColumn,
1668 static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node,
1669 const float availableWidth,
1670 const float availableHeight,
1671 const YGMeasureMode widthMeasureMode,
1672 const YGMeasureMode heightMeasureMode,
1673 const float ownerWidth,
1674 const float ownerHeight) {
1675 if ((!YGFloatIsUndefined(availableWidth) &&
1676 widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) ||
1677 (!YGFloatIsUndefined(availableHeight) &&
1678 heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) ||
1679 (widthMeasureMode == YGMeasureModeExactly &&
1680 heightMeasureMode == YGMeasureModeExactly)) {
1681 const float& marginAxisColumn = YGUnwrapFloatOptional(
1682 node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1683 const float& marginAxisRow = YGUnwrapFloatOptional(
1684 node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1686 node->setLayoutMeasuredDimension(
1690 YGFloatIsUndefined(availableWidth) ||
1691 (widthMeasureMode == YGMeasureModeAtMost &&
1692 availableWidth < 0.0f)
1694 : availableWidth - marginAxisRow,
1699 node->setLayoutMeasuredDimension(
1702 YGFlexDirectionColumn,
1703 YGFloatIsUndefined(availableHeight) ||
1704 (heightMeasureMode == YGMeasureModeAtMost &&
1705 availableHeight < 0.0f)
1707 : availableHeight - marginAxisColumn,
1717 static void YGZeroOutLayoutRecursivly(const YGNodeRef node) {
1718 memset(&(node->getLayout()), 0, sizeof(YGLayout));
1719 node->setHasNewLayout(true);
1720 node->cloneChildrenIfNeeded();
1721 const uint32_t childCount = YGNodeGetChildCount(node);
1722 for (uint32_t i = 0; i < childCount; i++) {
1723 const YGNodeRef child = node->getChild(i);
1724 YGZeroOutLayoutRecursivly(child);
1728 static float YGNodeCalculateAvailableInnerDim(
1729 const YGNodeRef node,
1730 YGFlexDirection axis,
1733 YGFlexDirection direction =
1734 YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn;
1735 YGDimension dimension =
1736 YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight;
1738 const float margin =
1739 YGUnwrapFloatOptional(node->getMarginForAxis(direction, ownerDim));
1740 const float paddingAndBorder =
1741 YGNodePaddingAndBorderForAxis(node, direction, ownerDim);
1743 float availableInnerDim = availableDim - margin - paddingAndBorder;
1744 // Max dimension overrides predefined dimension value; Min dimension in turn
1745 // overrides both of the above
1746 if (!YGFloatIsUndefined(availableInnerDim)) {
1747 // We want to make sure our available height does not violate min and max
1749 const YGFloatOptional minDimensionOptional = YGResolveValue(node->getStyle().minDimensions[dimension], ownerDim);
1750 const float minInnerDim = minDimensionOptional.isUndefined()
1752 : minDimensionOptional.getValue() - paddingAndBorder;
1754 const YGFloatOptional maxDimensionOptional = YGResolveValue(node->getStyle().maxDimensions[dimension], ownerDim) ;
1756 const float maxInnerDim = maxDimensionOptional.isUndefined()
1758 : maxDimensionOptional.getValue() - paddingAndBorder;
1760 YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim);
1763 return availableInnerDim;
1766 static void YGNodeComputeFlexBasisForChildren(
1767 const YGNodeRef node,
1768 const float availableInnerWidth,
1769 const float availableInnerHeight,
1770 YGMeasureMode widthMeasureMode,
1771 YGMeasureMode heightMeasureMode,
1772 YGDirection direction,
1773 YGFlexDirection mainAxis,
1774 const YGConfigRef config,
1776 float& totalOuterFlexBasis) {
1777 YGNodeRef singleFlexChild = nullptr;
1778 YGVector children = node->getChildren();
1779 YGMeasureMode measureModeMainDim =
1780 YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode;
1781 // If there is only one child with flexGrow + flexShrink it means we can set
1782 // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the
1783 // child to exactly match the remaining space
1784 if (measureModeMainDim == YGMeasureModeExactly) {
1785 for (auto child : children) {
1786 if (singleFlexChild != nullptr) {
1787 if (child->isNodeFlexible()) {
1788 // There is already a flexible child, abort
1789 singleFlexChild = nullptr;
1793 child->resolveFlexGrow() > 0.0f &&
1794 child->resolveFlexShrink() > 0.0f) {
1795 singleFlexChild = child;
1800 for (auto child : children) {
1801 child->resolveDimension();
1802 if (child->getStyle().display == YGDisplayNone) {
1803 YGZeroOutLayoutRecursivly(child);
1804 child->setHasNewLayout(true);
1805 child->setDirty(false);
1808 if (performLayout) {
1809 // Set the initial position (relative to the owner).
1810 const YGDirection childDirection = child->resolveDirection(direction);
1811 const float mainDim = YGFlexDirectionIsRow(mainAxis)
1812 ? availableInnerWidth
1813 : availableInnerHeight;
1814 const float crossDim = YGFlexDirectionIsRow(mainAxis)
1815 ? availableInnerHeight
1816 : availableInnerWidth;
1818 childDirection, mainDim, crossDim, availableInnerWidth);
1821 if (child->getStyle().positionType == YGPositionTypeAbsolute) {
1824 if (child == singleFlexChild) {
1825 child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
1826 child->setLayoutComputedFlexBasis(YGFloatOptional(0));
1828 YGNodeComputeFlexBasisForChild(
1831 availableInnerWidth,
1833 availableInnerHeight,
1834 availableInnerWidth,
1835 availableInnerHeight,
1841 totalOuterFlexBasis += YGUnwrapFloatOptional(
1842 child->getLayout().computedFlexBasis +
1843 child->getMarginForAxis(mainAxis, availableInnerWidth));
1847 // This function assumes that all the children of node have their
1848 // computedFlexBasis properly computed(To do this use
1849 // YGNodeComputeFlexBasisForChildren function).
1850 // This function calculates YGCollectFlexItemsRowMeasurement
1851 static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
1852 const YGNodeRef& node,
1853 const YGDirection ownerDirection,
1854 const float mainAxisownerSize,
1855 const float availableInnerWidth,
1856 const float availableInnerMainDim,
1857 const uint32_t startOfLineIndex,
1858 const uint32_t lineCount) {
1859 YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {};
1860 flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
1862 float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
1863 const YGFlexDirection mainAxis = YGResolveFlexDirection(
1864 node->getStyle().flexDirection, node->resolveDirection(ownerDirection));
1865 const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
1867 // Add items to the current line until it's full or we run out of items.
1868 uint32_t endOfLineIndex = startOfLineIndex;
1869 for (; endOfLineIndex < node->getChildrenCount(); endOfLineIndex++) {
1870 const YGNodeRef child = node->getChild(endOfLineIndex);
1871 if (child->getStyle().display == YGDisplayNone ||
1872 child->getStyle().positionType == YGPositionTypeAbsolute) {
1875 child->setLineIndex(lineCount);
1876 const float childMarginMainAxis = YGUnwrapFloatOptional(
1877 child->getMarginForAxis(mainAxis, availableInnerWidth));
1878 const float flexBasisWithMinAndMaxConstraints =
1879 YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
1882 YGUnwrapFloatOptional(child->getLayout().computedFlexBasis),
1883 mainAxisownerSize));
1885 // If this is a multi-line flow and this item pushes us over the
1886 // available size, we've
1887 // hit the end of the current line. Break out of the loop and lay out
1888 // the current line.
1889 if (sizeConsumedOnCurrentLineIncludingMinConstraint +
1890 flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
1891 availableInnerMainDim &&
1892 isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
1896 sizeConsumedOnCurrentLineIncludingMinConstraint +=
1897 flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
1898 flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
1899 flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
1900 flexAlgoRowMeasurement.itemsOnLine++;
1902 if (child->isNodeFlexible()) {
1903 flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow();
1905 // Unlike the grow factor, the shrink factor is scaled relative to the
1907 flexAlgoRowMeasurement.totalFlexShrinkScaledFactors +=
1908 -child->resolveFlexShrink() *
1909 YGUnwrapFloatOptional(child->getLayout().computedFlexBasis);
1912 flexAlgoRowMeasurement.relativeChildren.push_back(child);
1915 // The total flex factor needs to be floored to 1.
1916 if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 &&
1917 flexAlgoRowMeasurement.totalFlexGrowFactors < 1) {
1918 flexAlgoRowMeasurement.totalFlexGrowFactors = 1;
1921 // The total flex shrink factor needs to be floored to 1.
1922 if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 &&
1923 flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) {
1924 flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1;
1926 flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex;
1927 return flexAlgoRowMeasurement;
1930 // It distributes the free space to the flexible items and ensures that the size
1931 // of the flex items abide the min and max constraints. At the end of this
1932 // function the child nodes would have proper size. Prior using this function
1933 // please ensure that YGDistributeFreeSpaceFirstPass is called.
1934 static float YGDistributeFreeSpaceSecondPass(
1935 YGCollectFlexItemsRowValues& collectedFlexItemsValues,
1936 const YGNodeRef node,
1937 const YGFlexDirection mainAxis,
1938 const YGFlexDirection crossAxis,
1939 const float mainAxisownerSize,
1940 const float availableInnerMainDim,
1941 const float availableInnerCrossDim,
1942 const float availableInnerWidth,
1943 const float availableInnerHeight,
1944 const bool flexBasisOverflows,
1945 const YGMeasureMode measureModeCrossDim,
1946 const bool performLayout,
1947 const YGConfigRef config) {
1948 float childFlexBasis = 0;
1949 float flexShrinkScaledFactor = 0;
1950 float flexGrowFactor = 0;
1951 float deltaFreeSpace = 0;
1952 const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1953 const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
1955 for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
1956 childFlexBasis = YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
1957 currentRelativeChild,
1959 YGUnwrapFloatOptional(
1960 currentRelativeChild->getLayout().computedFlexBasis),
1961 mainAxisownerSize));
1962 float updatedMainSize = childFlexBasis;
1964 if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
1965 collectedFlexItemsValues.remainingFreeSpace < 0) {
1966 flexShrinkScaledFactor =
1967 -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
1968 // Is this child able to shrink?
1969 if (flexShrinkScaledFactor != 0) {
1972 if (!YGFloatIsUndefined(
1973 collectedFlexItemsValues.totalFlexShrinkScaledFactors) &&
1974 collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
1975 childSize = childFlexBasis + flexShrinkScaledFactor;
1977 childSize = childFlexBasis +
1978 (collectedFlexItemsValues.remainingFreeSpace /
1979 collectedFlexItemsValues.totalFlexShrinkScaledFactors) *
1980 flexShrinkScaledFactor;
1983 updatedMainSize = YGNodeBoundAxis(
1984 currentRelativeChild,
1987 availableInnerMainDim,
1988 availableInnerWidth);
1991 !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
1992 collectedFlexItemsValues.remainingFreeSpace > 0) {
1993 flexGrowFactor = currentRelativeChild->resolveFlexGrow();
1995 // Is this child able to grow?
1996 if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
1997 updatedMainSize = YGNodeBoundAxis(
1998 currentRelativeChild,
2001 collectedFlexItemsValues.remainingFreeSpace /
2002 collectedFlexItemsValues.totalFlexGrowFactors *
2004 availableInnerMainDim,
2005 availableInnerWidth);
2009 deltaFreeSpace += updatedMainSize - childFlexBasis;
2011 const float marginMain = YGUnwrapFloatOptional(
2012 currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth));
2013 const float marginCross = YGUnwrapFloatOptional(
2014 currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth));
2016 float childCrossSize;
2017 float childMainSize = updatedMainSize + marginMain;
2018 YGMeasureMode childCrossMeasureMode;
2019 YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
2021 if (!currentRelativeChild->getStyle().aspectRatio.isUndefined()) {
2022 childCrossSize = isMainAxisRow ? (childMainSize - marginMain) /
2023 currentRelativeChild->getStyle().aspectRatio.getValue()
2024 : (childMainSize - marginMain) *
2025 currentRelativeChild->getStyle().aspectRatio.getValue();
2026 childCrossMeasureMode = YGMeasureModeExactly;
2028 childCrossSize += marginCross;
2030 !YGFloatIsUndefined(availableInnerCrossDim) &&
2031 !YGNodeIsStyleDimDefined(
2032 currentRelativeChild, crossAxis, availableInnerCrossDim) &&
2033 measureModeCrossDim == YGMeasureModeExactly &&
2034 !(isNodeFlexWrap && flexBasisOverflows) &&
2035 YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
2036 currentRelativeChild->marginLeadingValue(crossAxis).unit !=
2038 currentRelativeChild->marginTrailingValue(crossAxis).unit !=
2040 childCrossSize = availableInnerCrossDim;
2041 childCrossMeasureMode = YGMeasureModeExactly;
2042 } else if (!YGNodeIsStyleDimDefined(
2043 currentRelativeChild, crossAxis, availableInnerCrossDim)) {
2044 childCrossSize = availableInnerCrossDim;
2045 childCrossMeasureMode = YGFloatIsUndefined(childCrossSize)
2046 ? YGMeasureModeUndefined
2047 : YGMeasureModeAtMost;
2050 YGUnwrapFloatOptional(YGResolveValue(
2051 currentRelativeChild->getResolvedDimension(dim[crossAxis]),
2052 availableInnerCrossDim)) +
2054 const bool isLoosePercentageMeasurement =
2055 currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit ==
2057 measureModeCrossDim != YGMeasureModeExactly;
2058 childCrossMeasureMode =
2059 YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement
2060 ? YGMeasureModeUndefined
2061 : YGMeasureModeExactly;
2064 YGConstrainMaxSizeForMode(
2065 currentRelativeChild,
2067 availableInnerMainDim,
2068 availableInnerWidth,
2069 &childMainMeasureMode,
2071 YGConstrainMaxSizeForMode(
2072 currentRelativeChild,
2074 availableInnerCrossDim,
2075 availableInnerWidth,
2076 &childCrossMeasureMode,
2079 const bool requiresStretchLayout =
2080 !YGNodeIsStyleDimDefined(
2081 currentRelativeChild, crossAxis, availableInnerCrossDim) &&
2082 YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
2083 currentRelativeChild->marginLeadingValue(crossAxis).unit !=
2085 currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
2087 const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
2088 const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
2090 const YGMeasureMode childWidthMeasureMode =
2091 isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
2092 const YGMeasureMode childHeightMeasureMode =
2093 !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
2095 // Recursively call the layout algorithm for this child with the updated
2097 YGLayoutNodeInternal(
2098 currentRelativeChild,
2101 node->getLayout().direction,
2102 childWidthMeasureMode,
2103 childHeightMeasureMode,
2104 availableInnerWidth,
2105 availableInnerHeight,
2106 performLayout && !requiresStretchLayout,
2109 node->setLayoutHadOverflow(
2110 node->getLayout().hadOverflow |
2111 currentRelativeChild->getLayout().hadOverflow);
2113 return deltaFreeSpace;
2116 // It distributes the free space to the flexible items.For those flexible items
2117 // whose min and max constraints are triggered, those flex item's clamped size
2118 // is removed from the remaingfreespace.
2119 static void YGDistributeFreeSpaceFirstPass(
2120 YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2121 const YGFlexDirection mainAxis,
2122 const float mainAxisownerSize,
2123 const float availableInnerMainDim,
2124 const float availableInnerWidth) {
2125 float flexShrinkScaledFactor = 0;
2126 float flexGrowFactor = 0;
2127 float baseMainSize = 0;
2128 float boundMainSize = 0;
2129 float deltaFreeSpace = 0;
2131 for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
2132 float childFlexBasis = YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
2133 currentRelativeChild,
2135 YGUnwrapFloatOptional(
2136 currentRelativeChild->getLayout().computedFlexBasis),
2137 mainAxisownerSize));
2139 if (collectedFlexItemsValues.remainingFreeSpace < 0) {
2140 flexShrinkScaledFactor =
2141 -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
2143 // Is this child able to shrink?
2144 if (!YGFloatIsUndefined(flexShrinkScaledFactor) &&
2145 flexShrinkScaledFactor != 0) {
2146 baseMainSize = childFlexBasis +
2147 collectedFlexItemsValues.remainingFreeSpace /
2148 collectedFlexItemsValues.totalFlexShrinkScaledFactors *
2149 flexShrinkScaledFactor;
2150 boundMainSize = YGNodeBoundAxis(
2151 currentRelativeChild,
2154 availableInnerMainDim,
2155 availableInnerWidth);
2156 if (!YGFloatIsUndefined(baseMainSize) &&
2157 !YGFloatIsUndefined(boundMainSize) &&
2158 baseMainSize != boundMainSize) {
2159 // By excluding this item's size and flex factor from remaining,
2161 // min/max constraints should also trigger in the second pass
2163 // item's size calculation being identical in the first and second
2165 deltaFreeSpace += boundMainSize - childFlexBasis;
2166 collectedFlexItemsValues.totalFlexShrinkScaledFactors -=
2167 flexShrinkScaledFactor;
2171 !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
2172 collectedFlexItemsValues.remainingFreeSpace > 0) {
2173 flexGrowFactor = currentRelativeChild->resolveFlexGrow();
2175 // Is this child able to grow?
2176 if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
2177 baseMainSize = childFlexBasis +
2178 collectedFlexItemsValues.remainingFreeSpace /
2179 collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor;
2180 boundMainSize = YGNodeBoundAxis(
2181 currentRelativeChild,
2184 availableInnerMainDim,
2185 availableInnerWidth);
2187 if (!YGFloatIsUndefined(baseMainSize) &&
2188 !YGFloatIsUndefined(boundMainSize) &&
2189 baseMainSize != boundMainSize) {
2190 // By excluding this item's size and flex factor from remaining,
2192 // min/max constraints should also trigger in the second pass
2194 // item's size calculation being identical in the first and second
2196 deltaFreeSpace += boundMainSize - childFlexBasis;
2197 collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor;
2202 collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace;
2205 // Do two passes over the flex items to figure out how to distribute the
2207 // The first pass finds the items whose min/max constraints trigger,
2208 // freezes them at those
2209 // sizes, and excludes those sizes from the remaining space. The second
2210 // pass sets the size
2211 // of each flexible item. It distributes the remaining space amongst the
2212 // items whose min/max
2213 // constraints didn't trigger in pass 1. For the other items, it sets
2214 // their sizes by forcing
2215 // their min/max constraints to trigger again.
2217 // This two pass approach for resolving min/max constraints deviates from
2219 // spec (https://www.w3.org/TR/YG-flexbox-1/#resolve-flexible-lengths)
2220 // describes a process
2221 // that needs to be repeated a variable number of times. The algorithm
2223 // won't handle all cases but it was simpler to implement and it mitigates
2225 // concerns because we know exactly how many passes it'll do.
2227 // At the end of this function the child nodes would have the proper size
2228 // assigned to them.
2230 static void YGResolveFlexibleLength(
2231 const YGNodeRef node,
2232 YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2233 const YGFlexDirection mainAxis,
2234 const YGFlexDirection crossAxis,
2235 const float mainAxisownerSize,
2236 const float availableInnerMainDim,
2237 const float availableInnerCrossDim,
2238 const float availableInnerWidth,
2239 const float availableInnerHeight,
2240 const bool flexBasisOverflows,
2241 const YGMeasureMode measureModeCrossDim,
2242 const bool performLayout,
2243 const YGConfigRef config) {
2244 const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace;
2245 // First pass: detect the flex items whose min/max constraints trigger
2246 YGDistributeFreeSpaceFirstPass(
2247 collectedFlexItemsValues,
2250 availableInnerMainDim,
2251 availableInnerWidth);
2253 // Second pass: resolve the sizes of the flexible items
2254 const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass(
2255 collectedFlexItemsValues,
2260 availableInnerMainDim,
2261 availableInnerCrossDim,
2262 availableInnerWidth,
2263 availableInnerHeight,
2265 measureModeCrossDim,
2269 collectedFlexItemsValues.remainingFreeSpace =
2270 originalFreeSpace - distributedFreeSpace;
2273 static void YGJustifyMainAxis(
2274 const YGNodeRef node,
2275 YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2276 const uint32_t& startOfLineIndex,
2277 const YGFlexDirection& mainAxis,
2278 const YGFlexDirection& crossAxis,
2279 const YGMeasureMode& measureModeMainDim,
2280 const YGMeasureMode& measureModeCrossDim,
2281 const float& mainAxisownerSize,
2282 const float& ownerWidth,
2283 const float& availableInnerMainDim,
2284 const float& availableInnerCrossDim,
2285 const float& availableInnerWidth,
2286 const bool& performLayout) {
2287 const YGStyle style = node->getStyle();
2289 // If we are using "at most" rules in the main axis. Calculate the remaining
2290 // space when constraint by the min size defined for the main axis.
2291 if (measureModeMainDim == YGMeasureModeAtMost &&
2292 collectedFlexItemsValues.remainingFreeSpace > 0) {
2293 if (style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined &&
2294 !YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize)
2296 collectedFlexItemsValues.remainingFreeSpace = YGFloatMax(
2298 YGUnwrapFloatOptional(YGResolveValue(
2299 style.minDimensions[dim[mainAxis]], mainAxisownerSize)) -
2300 (availableInnerMainDim -
2301 collectedFlexItemsValues.remainingFreeSpace));
2303 collectedFlexItemsValues.remainingFreeSpace = 0;
2307 int numberOfAutoMarginsOnCurrentLine = 0;
2308 for (uint32_t i = startOfLineIndex;
2309 i < collectedFlexItemsValues.endOfLineIndex;
2311 const YGNodeRef child = node->getChild(i);
2312 if (child->getStyle().positionType == YGPositionTypeRelative) {
2313 if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
2314 numberOfAutoMarginsOnCurrentLine++;
2316 if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
2317 numberOfAutoMarginsOnCurrentLine++;
2322 // In order to position the elements in the main axis, we have two
2323 // controls. The space between the beginning and the first element
2324 // and the space between each two elements.
2325 float leadingMainDim = 0;
2326 float betweenMainDim = 0;
2327 const YGJustify justifyContent = node->getStyle().justifyContent;
2329 if (numberOfAutoMarginsOnCurrentLine == 0) {
2330 switch (justifyContent) {
2331 case YGJustifyCenter:
2332 leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2;
2334 case YGJustifyFlexEnd:
2335 leadingMainDim = collectedFlexItemsValues.remainingFreeSpace;
2337 case YGJustifySpaceBetween:
2338 if (collectedFlexItemsValues.itemsOnLine > 1) {
2340 YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
2341 (collectedFlexItemsValues.itemsOnLine - 1);
2346 case YGJustifySpaceEvenly:
2347 // Space is distributed evenly across all elements
2348 betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2349 (collectedFlexItemsValues.itemsOnLine + 1);
2350 leadingMainDim = betweenMainDim;
2352 case YGJustifySpaceAround:
2353 // Space on the edges is half of the space between elements
2354 betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2355 collectedFlexItemsValues.itemsOnLine;
2356 leadingMainDim = betweenMainDim / 2;
2358 case YGJustifyFlexStart:
2363 const float leadingPaddingAndBorderMain = YGUnwrapFloatOptional(
2364 node->getLeadingPaddingAndBorder(mainAxis, ownerWidth));
2365 collectedFlexItemsValues.mainDim =
2366 leadingPaddingAndBorderMain + leadingMainDim;
2367 collectedFlexItemsValues.crossDim = 0;
2369 for (uint32_t i = startOfLineIndex;
2370 i < collectedFlexItemsValues.endOfLineIndex;
2372 const YGNodeRef child = node->getChild(i);
2373 const YGStyle childStyle = child->getStyle();
2374 const YGLayout childLayout = child->getLayout();
2375 if (childStyle.display == YGDisplayNone) {
2378 if (childStyle.positionType == YGPositionTypeAbsolute &&
2379 child->isLeadingPositionDefined(mainAxis)) {
2380 if (performLayout) {
2381 // In case the child is position absolute and has left/top being
2382 // defined, we override the position to whatever the user said
2383 // (and margin/border).
2384 child->setLayoutPosition(
2385 YGUnwrapFloatOptional(
2386 child->getLeadingPosition(mainAxis, availableInnerMainDim)) +
2387 node->getLeadingBorder(mainAxis) +
2388 YGUnwrapFloatOptional(
2389 child->getLeadingMargin(mainAxis, availableInnerWidth)),
2393 // Now that we placed the element, we need to update the variables.
2394 // We need to do that only for relative elements. Absolute elements
2395 // do not take part in that phase.
2396 if (childStyle.positionType == YGPositionTypeRelative) {
2397 if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
2398 collectedFlexItemsValues.mainDim +=
2399 collectedFlexItemsValues.remainingFreeSpace /
2400 numberOfAutoMarginsOnCurrentLine;
2403 if (performLayout) {
2404 child->setLayoutPosition(
2405 childLayout.position[pos[mainAxis]] +
2406 collectedFlexItemsValues.mainDim,
2410 if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
2411 collectedFlexItemsValues.mainDim +=
2412 collectedFlexItemsValues.remainingFreeSpace /
2413 numberOfAutoMarginsOnCurrentLine;
2416 !performLayout && measureModeCrossDim == YGMeasureModeExactly;
2418 // If we skipped the flex step, then we can't rely on the
2419 // measuredDims because
2420 // they weren't computed. This means we can't call
2421 // YGNodeDimWithMargin.
2422 collectedFlexItemsValues.mainDim += betweenMainDim +
2423 YGUnwrapFloatOptional(child->getMarginForAxis(
2424 mainAxis, availableInnerWidth)) +
2425 YGUnwrapFloatOptional(childLayout.computedFlexBasis);
2426 collectedFlexItemsValues.crossDim = availableInnerCrossDim;
2428 // The main dimension is the sum of all the elements dimension plus
2430 collectedFlexItemsValues.mainDim += betweenMainDim +
2431 YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);
2433 // The cross dimension is the max of the elements dimension since
2434 // there can only be one element in that cross dimension.
2435 collectedFlexItemsValues.crossDim = YGFloatMax(
2436 collectedFlexItemsValues.crossDim,
2437 YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));
2439 } else if (performLayout) {
2440 child->setLayoutPosition(
2441 childLayout.position[pos[mainAxis]] +
2442 node->getLeadingBorder(mainAxis) + leadingMainDim,
2447 collectedFlexItemsValues.mainDim += YGUnwrapFloatOptional(
2448 node->getTrailingPaddingAndBorder(mainAxis, ownerWidth));
2452 // This is the main routine that implements a subset of the flexbox layout
2454 // described in the W3C YG documentation: https://www.w3.org/TR/YG3-flexbox/.
2456 // Limitations of this algorithm, compared to the full standard:
2457 // * Display property is always assumed to be 'flex' except for Text nodes,
2459 // are assumed to be 'inline-flex'.
2460 // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes
2462 // stacked in document order.
2463 // * The 'order' property is not supported. The order of flex items is always
2465 // by document order.
2466 // * The 'visibility' property is always assumed to be 'visible'. Values of
2468 // and 'hidden' are not supported.
2469 // * There is no support for forced breaks.
2470 // * It does not support vertical inline directions (top-to-bottom or
2471 // bottom-to-top text).
2473 // Deviations from standard:
2474 // * Section 4.5 of the spec indicates that all flex items have a default
2476 // main size. For text blocks, for example, this is the width of the widest
2478 // Calculating the minimum width is expensive, so we forego it and assume a
2480 // minimum main size of 0.
2481 // * Min/Max sizes in the main axis are not honored when resolving flexible
2483 // * The spec indicates that the default value for 'flexDirection' is 'row',
2485 // the algorithm below assumes a default of 'column'.
2487 // Input parameters:
2488 // - node: current node to be sized and layed out
2489 // - availableWidth & availableHeight: available size to be used for sizing
2491 // or YGUndefined if the size is not available; interpretation depends on
2494 // - ownerDirection: the inline (text) direction within the owner
2495 // (left-to-right or
2497 // - widthMeasureMode: indicates the sizing rules for the width (see below
2499 // - heightMeasureMode: indicates the sizing rules for the height (see below
2501 // - performLayout: specifies whether the caller is interested in just the
2503 // of the node or it requires the entire node and its subtree to be layed
2505 // (with final positions)
2508 // This routine is called recursively to lay out subtrees of flexbox
2509 // elements. It uses the
2510 // information in node.style, which is treated as a read-only input. It is
2512 // setting the layout.direction and layout.measuredDimensions fields for the
2513 // input node as well
2514 // as the layout.position and layout.lineIndex fields for its child nodes.
2516 // layout.measuredDimensions field includes any border or padding for the
2518 // not include margins.
2520 // The spec describes four different layout modes: "fill available", "max
2523 // and "fit content". Of these, we don't use "min content" because we don't
2525 // minimum main sizes (see above for details). Each of our measure modes maps
2527 // from the spec (https://www.w3.org/TR/YG3-sizing/#terms):
2528 // - YGMeasureModeUndefined: max content
2529 // - YGMeasureModeExactly: fill available
2530 // - YGMeasureModeAtMost: fit content
2532 // When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes
2533 // an available size of
2534 // undefined then it must also pass a measure mode of YGMeasureModeUndefined
2535 // in that dimension.
2537 static void YGNodelayoutImpl(const YGNodeRef node,
2538 const float availableWidth,
2539 const float availableHeight,
2540 const YGDirection ownerDirection,
2541 const YGMeasureMode widthMeasureMode,
2542 const YGMeasureMode heightMeasureMode,
2543 const float ownerWidth,
2544 const float ownerHeight,
2545 const bool performLayout,
2546 const YGConfigRef config) {
2547 YGAssertWithNode(node,
2548 YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined
2550 "availableWidth is indefinite so widthMeasureMode must be "
2551 "YGMeasureModeUndefined");
2552 YGAssertWithNode(node,
2553 YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined
2555 "availableHeight is indefinite so heightMeasureMode must be "
2556 "YGMeasureModeUndefined");
2558 // Set the resolved resolution in the node's layout.
2559 const YGDirection direction = node->resolveDirection(ownerDirection);
2560 node->setLayoutDirection(direction);
2562 const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction);
2563 const YGFlexDirection flexColumnDirection =
2564 YGResolveFlexDirection(YGFlexDirectionColumn, direction);
2566 node->setLayoutMargin(
2567 YGUnwrapFloatOptional(
2568 node->getLeadingMargin(flexRowDirection, ownerWidth)),
2570 node->setLayoutMargin(
2571 YGUnwrapFloatOptional(
2572 node->getTrailingMargin(flexRowDirection, ownerWidth)),
2574 node->setLayoutMargin(
2575 YGUnwrapFloatOptional(
2576 node->getLeadingMargin(flexColumnDirection, ownerWidth)),
2578 node->setLayoutMargin(
2579 YGUnwrapFloatOptional(
2580 node->getTrailingMargin(flexColumnDirection, ownerWidth)),
2583 node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), YGEdgeStart);
2584 node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), YGEdgeEnd);
2585 node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop);
2586 node->setLayoutBorder(
2587 node->getTrailingBorder(flexColumnDirection), YGEdgeBottom);
2589 node->setLayoutPadding(
2590 YGUnwrapFloatOptional(
2591 node->getLeadingPadding(flexRowDirection, ownerWidth)),
2593 node->setLayoutPadding(
2594 YGUnwrapFloatOptional(
2595 node->getTrailingPadding(flexRowDirection, ownerWidth)),
2597 node->setLayoutPadding(
2598 YGUnwrapFloatOptional(
2599 node->getLeadingPadding(flexColumnDirection, ownerWidth)),
2601 node->setLayoutPadding(
2602 YGUnwrapFloatOptional(
2603 node->getTrailingPadding(flexColumnDirection, ownerWidth)),
2606 if (node->getMeasure() != nullptr) {
2607 YGNodeWithMeasureFuncSetMeasuredDimensions(node,
2617 const uint32_t childCount = YGNodeGetChildCount(node);
2618 if (childCount == 0) {
2619 YGNodeEmptyContainerSetMeasuredDimensions(node,
2629 // If we're not being asked to perform a full layout we can skip the algorithm if we already know
2631 if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node,
2641 // At this point we know we're going to perform work. Ensure that each child has a mutable copy.
2642 node->cloneChildrenIfNeeded();
2643 // Reset layout flags, as they could have changed.
2644 node->setLayoutHadOverflow(false);
2646 // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
2647 const YGFlexDirection mainAxis =
2648 YGResolveFlexDirection(node->getStyle().flexDirection, direction);
2649 const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
2650 const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
2651 const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
2653 const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
2654 const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth;
2656 const float leadingPaddingAndBorderCross = YGUnwrapFloatOptional(
2657 node->getLeadingPaddingAndBorder(crossAxis, ownerWidth));
2658 const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth);
2659 const float paddingAndBorderAxisCross =
2660 YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth);
2662 YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;
2663 YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;
2665 const float paddingAndBorderAxisRow =
2666 isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;
2667 const float paddingAndBorderAxisColumn =
2668 isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;
2670 const float marginAxisRow = YGUnwrapFloatOptional(
2671 node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
2672 const float marginAxisColumn = YGUnwrapFloatOptional(
2673 node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
2675 const float minInnerWidth =
2676 YGUnwrapFloatOptional(YGResolveValue(node->getStyle().minDimensions[YGDimensionWidth], ownerWidth)) -
2677 paddingAndBorderAxisRow;
2678 const float maxInnerWidth =
2679 YGUnwrapFloatOptional(YGResolveValue(node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)) -
2680 paddingAndBorderAxisRow;
2681 const float minInnerHeight =
2682 YGUnwrapFloatOptional(YGResolveValue(node->getStyle().minDimensions[YGDimensionHeight], ownerHeight)) -
2683 paddingAndBorderAxisColumn;
2684 const float maxInnerHeight =
2685 YGUnwrapFloatOptional(YGResolveValue(
2686 node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight)) -
2687 paddingAndBorderAxisColumn;
2689 const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight;
2690 const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight;
2692 // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
2694 float availableInnerWidth = YGNodeCalculateAvailableInnerDim(
2695 node, YGFlexDirectionRow, availableWidth, ownerWidth);
2696 float availableInnerHeight = YGNodeCalculateAvailableInnerDim(
2697 node, YGFlexDirectionColumn, availableHeight, ownerHeight);
2699 float availableInnerMainDim =
2700 isMainAxisRow ? availableInnerWidth : availableInnerHeight;
2701 const float availableInnerCrossDim =
2702 isMainAxisRow ? availableInnerHeight : availableInnerWidth;
2704 float totalOuterFlexBasis = 0;
2706 // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
2708 YGNodeComputeFlexBasisForChildren(
2710 availableInnerWidth,
2711 availableInnerHeight,
2718 totalOuterFlexBasis);
2720 const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined
2722 : totalOuterFlexBasis > availableInnerMainDim;
2723 if (isNodeFlexWrap && flexBasisOverflows &&
2724 measureModeMainDim == YGMeasureModeAtMost) {
2725 measureModeMainDim = YGMeasureModeExactly;
2727 // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
2729 // Indexes of children that represent the first and last items in the line.
2730 uint32_t startOfLineIndex = 0;
2731 uint32_t endOfLineIndex = 0;
2734 uint32_t lineCount = 0;
2736 // Accumulated cross dimensions of all lines so far.
2737 float totalLineCrossDim = 0;
2739 // Max main dimension of all the lines.
2740 float maxLineMainDim = 0;
2741 YGCollectFlexItemsRowValues collectedFlexItemsValues;
2742 for (; endOfLineIndex < childCount;
2743 lineCount++, startOfLineIndex = endOfLineIndex) {
2744 collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues(
2748 availableInnerWidth,
2749 availableInnerMainDim,
2752 endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
2754 // If we don't need to measure the cross axis, we can skip the entire flex
2756 const bool canSkipFlex =
2757 !performLayout && measureModeCrossDim == YGMeasureModeExactly;
2759 // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
2760 // Calculate the remaining available space that needs to be allocated.
2761 // If the main dimension size isn't known, it is computed based on
2762 // the line length, so there's no more space left to distribute.
2764 bool sizeBasedOnContent = false;
2765 // If we don't measure with exact main dimension we want to ensure we don't violate min and max
2766 if (measureModeMainDim != YGMeasureModeExactly) {
2767 if (!YGFloatIsUndefined(minInnerMainDim) &&
2768 collectedFlexItemsValues.sizeConsumedOnCurrentLine <
2770 availableInnerMainDim = minInnerMainDim;
2772 !YGFloatIsUndefined(maxInnerMainDim) &&
2773 collectedFlexItemsValues.sizeConsumedOnCurrentLine >
2775 availableInnerMainDim = maxInnerMainDim;
2777 if (!node->getConfig()->useLegacyStretchBehaviour &&
2778 ((YGFloatIsUndefined(
2779 collectedFlexItemsValues.totalFlexGrowFactors) &&
2780 collectedFlexItemsValues.totalFlexGrowFactors == 0) ||
2781 (YGFloatIsUndefined(node->resolveFlexGrow()) &&
2782 node->resolveFlexGrow() == 0))) {
2783 // If we don't have any children to flex or we can't flex the node
2784 // itself, space we've used is all space we need. Root node also
2785 // should be shrunk to minimum
2786 availableInnerMainDim =
2787 collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2790 if (node->getConfig()->useLegacyStretchBehaviour) {
2791 node->setLayoutDidUseLegacyFlag(true);
2793 sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour;
2797 if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) {
2798 collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim -
2799 collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2800 } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) {
2801 // availableInnerMainDim is indefinite which means the node is being sized based on its
2803 // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for
2804 // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
2805 collectedFlexItemsValues.remainingFreeSpace =
2806 -collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2810 YGResolveFlexibleLength(
2812 collectedFlexItemsValues,
2816 availableInnerMainDim,
2817 availableInnerCrossDim,
2818 availableInnerWidth,
2819 availableInnerHeight,
2821 measureModeCrossDim,
2826 node->setLayoutHadOverflow(
2827 node->getLayout().hadOverflow |
2828 (collectedFlexItemsValues.remainingFreeSpace < 0));
2830 // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
2832 // At this point, all the children have their dimensions set in the main
2834 // Their dimensions are also set in the cross axis with the exception of
2836 // that are aligned "stretch". We need to compute these stretch values and
2837 // set the final positions.
2841 collectedFlexItemsValues,
2846 measureModeCrossDim,
2849 availableInnerMainDim,
2850 availableInnerCrossDim,
2851 availableInnerWidth,
2854 float containerCrossAxis = availableInnerCrossDim;
2855 if (measureModeCrossDim == YGMeasureModeUndefined ||
2856 measureModeCrossDim == YGMeasureModeAtMost) {
2857 // Compute the cross axis from the max cross dimension of the children.
2858 containerCrossAxis =
2862 collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
2865 paddingAndBorderAxisCross;
2868 // If there's no flex wrap, the cross dimension is defined by the container.
2869 if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {
2870 collectedFlexItemsValues.crossDim = availableInnerCrossDim;
2873 // Clamp to the min/max size specified on the container.
2874 collectedFlexItemsValues.crossDim =
2878 collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
2881 paddingAndBorderAxisCross;
2883 // STEP 7: CROSS-AXIS ALIGNMENT
2884 // We can skip child alignment if we're just measuring the container.
2885 if (performLayout) {
2886 for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {
2887 const YGNodeRef child = node->getChild(i);
2888 if (child->getStyle().display == YGDisplayNone) {
2891 if (child->getStyle().positionType == YGPositionTypeAbsolute) {
2892 // If the child is absolutely positioned and has a
2893 // top/left/bottom/right set, override
2894 // all the previously computed positions to set it correctly.
2895 const bool isChildLeadingPosDefined =
2896 child->isLeadingPositionDefined(crossAxis);
2897 if (isChildLeadingPosDefined) {
2898 child->setLayoutPosition(
2899 YGUnwrapFloatOptional(child->getLeadingPosition(
2900 crossAxis, availableInnerCrossDim)) +
2901 node->getLeadingBorder(crossAxis) +
2902 YGUnwrapFloatOptional(child->getLeadingMargin(
2903 crossAxis, availableInnerWidth)),
2906 // If leading position is not defined or calculations result in Nan, default to border + margin
2907 if (!isChildLeadingPosDefined ||
2908 YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) {
2909 child->setLayoutPosition(
2910 node->getLeadingBorder(crossAxis) +
2911 YGUnwrapFloatOptional(child->getLeadingMargin(
2912 crossAxis, availableInnerWidth)),
2916 float leadingCrossDim = leadingPaddingAndBorderCross;
2918 // For a relative children, we're either using alignItems (owner) or
2919 // alignSelf (child) in order to determine the position in the cross
2921 const YGAlign alignItem = YGNodeAlignItem(node, child);
2923 // If the child uses align stretch, we need to lay it out one more
2925 // forcing the cross-axis size to be the computed cross size for the
2927 if (alignItem == YGAlignStretch &&
2928 child->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
2929 child->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
2930 // If the child defines a definite size for its cross axis, there's
2931 // no need to stretch.
2932 if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {
2933 float childMainSize =
2934 child->getLayout().measuredDimensions[dim[mainAxis]];
2935 float childCrossSize =
2936 !child->getStyle().aspectRatio.isUndefined()
2937 ? ((YGUnwrapFloatOptional(child->getMarginForAxis(
2938 crossAxis, availableInnerWidth)) +
2939 (isMainAxisRow ? childMainSize /
2940 child->getStyle().aspectRatio.getValue()
2942 child->getStyle().aspectRatio.getValue())))
2943 : collectedFlexItemsValues.crossDim;
2945 childMainSize += YGUnwrapFloatOptional(
2946 child->getMarginForAxis(mainAxis, availableInnerWidth));
2948 YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
2949 YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly;
2950 YGConstrainMaxSizeForMode(child,
2952 availableInnerMainDim,
2953 availableInnerWidth,
2954 &childMainMeasureMode,
2956 YGConstrainMaxSizeForMode(child,
2958 availableInnerCrossDim,
2959 availableInnerWidth,
2960 &childCrossMeasureMode,
2963 const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
2964 const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
2966 const YGMeasureMode childWidthMeasureMode =
2967 YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined
2968 : YGMeasureModeExactly;
2969 const YGMeasureMode childHeightMeasureMode =
2970 YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined
2971 : YGMeasureModeExactly;
2973 YGLayoutNodeInternal(
2978 childWidthMeasureMode,
2979 childHeightMeasureMode,
2980 availableInnerWidth,
2981 availableInnerHeight,
2987 const float remainingCrossDim = containerCrossAxis -
2988 YGNodeDimWithMargin(child, crossAxis, availableInnerWidth);
2990 if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto &&
2991 child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
2992 leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2);
2994 child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
2997 child->marginLeadingValue(crossAxis).unit == YGUnitAuto) {
2998 leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim);
2999 } else if (alignItem == YGAlignFlexStart) {
3001 } else if (alignItem == YGAlignCenter) {
3002 leadingCrossDim += remainingCrossDim / 2;
3004 leadingCrossDim += remainingCrossDim;
3007 // And we apply the position
3008 child->setLayoutPosition(
3009 child->getLayout().position[pos[crossAxis]] + totalLineCrossDim +
3016 totalLineCrossDim += collectedFlexItemsValues.crossDim;
3018 YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
3021 // STEP 8: MULTI-LINE CONTENT ALIGNMENT
3022 if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) &&
3023 !YGFloatIsUndefined(availableInnerCrossDim)) {
3024 const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;
3026 float crossDimLead = 0;
3027 float currentLead = leadingPaddingAndBorderCross;
3029 switch (node->getStyle().alignContent) {
3030 case YGAlignFlexEnd:
3031 currentLead += remainingAlignContentDim;
3034 currentLead += remainingAlignContentDim / 2;
3036 case YGAlignStretch:
3037 if (availableInnerCrossDim > totalLineCrossDim) {
3038 crossDimLead = remainingAlignContentDim / lineCount;
3041 case YGAlignSpaceAround:
3042 if (availableInnerCrossDim > totalLineCrossDim) {
3043 currentLead += remainingAlignContentDim / (2 * lineCount);
3044 if (lineCount > 1) {
3045 crossDimLead = remainingAlignContentDim / lineCount;
3048 currentLead += remainingAlignContentDim / 2;
3051 case YGAlignSpaceBetween:
3052 if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {
3053 crossDimLead = remainingAlignContentDim / (lineCount - 1);
3057 case YGAlignFlexStart:
3058 case YGAlignBaseline:
3062 uint32_t endIndex = 0;
3063 for (uint32_t i = 0; i < lineCount; i++) {
3064 const uint32_t startIndex = endIndex;
3067 // compute the line's height and find the endIndex
3068 float lineHeight = 0;
3069 float maxAscentForCurrentLine = 0;
3070 float maxDescentForCurrentLine = 0;
3071 for (ii = startIndex; ii < childCount; ii++) {
3072 const YGNodeRef child = node->getChild(ii);
3073 if (child->getStyle().display == YGDisplayNone) {
3076 if (child->getStyle().positionType == YGPositionTypeRelative) {
3077 if (child->getLineIndex() != i) {
3080 if (YGNodeIsLayoutDimDefined(child, crossAxis)) {
3081 lineHeight = YGFloatMax(
3083 child->getLayout().measuredDimensions[dim[crossAxis]] +
3084 YGUnwrapFloatOptional(child->getMarginForAxis(
3085 crossAxis, availableInnerWidth)));
3087 if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
3088 const float ascent = YGBaseline(child) +
3089 YGUnwrapFloatOptional(child->getLeadingMargin(
3090 YGFlexDirectionColumn, availableInnerWidth));
3091 const float descent =
3092 child->getLayout().measuredDimensions[YGDimensionHeight] +
3093 YGUnwrapFloatOptional(child->getMarginForAxis(
3094 YGFlexDirectionColumn, availableInnerWidth)) -
3096 maxAscentForCurrentLine =
3097 YGFloatMax(maxAscentForCurrentLine, ascent);
3098 maxDescentForCurrentLine =
3099 YGFloatMax(maxDescentForCurrentLine, descent);
3100 lineHeight = YGFloatMax(
3101 lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);
3106 lineHeight += crossDimLead;
3108 if (performLayout) {
3109 for (ii = startIndex; ii < endIndex; ii++) {
3110 const YGNodeRef child = node->getChild(ii);
3111 if (child->getStyle().display == YGDisplayNone) {
3114 if (child->getStyle().positionType == YGPositionTypeRelative) {
3115 switch (YGNodeAlignItem(node, child)) {
3116 case YGAlignFlexStart: {
3117 child->setLayoutPosition(
3119 YGUnwrapFloatOptional(child->getLeadingMargin(
3120 crossAxis, availableInnerWidth)),
3124 case YGAlignFlexEnd: {
3125 child->setLayoutPosition(
3126 currentLead + lineHeight -
3127 YGUnwrapFloatOptional(child->getTrailingMargin(
3128 crossAxis, availableInnerWidth)) -
3129 child->getLayout().measuredDimensions[dim[crossAxis]],
3133 case YGAlignCenter: {
3135 child->getLayout().measuredDimensions[dim[crossAxis]];
3137 child->setLayoutPosition(
3138 currentLead + (lineHeight - childHeight) / 2,
3142 case YGAlignStretch: {
3143 child->setLayoutPosition(
3145 YGUnwrapFloatOptional(child->getLeadingMargin(
3146 crossAxis, availableInnerWidth)),
3149 // Remeasure child with the line height as it as been only measured with the
3150 // owners height yet.
3151 if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {
3152 const float childWidth = isMainAxisRow
3153 ? (child->getLayout()
3154 .measuredDimensions[YGDimensionWidth] +
3155 YGUnwrapFloatOptional(child->getMarginForAxis(
3156 mainAxis, availableInnerWidth)))
3159 const float childHeight = !isMainAxisRow
3160 ? (child->getLayout()
3161 .measuredDimensions[YGDimensionHeight] +
3162 YGUnwrapFloatOptional(child->getMarginForAxis(
3163 crossAxis, availableInnerWidth)))
3166 if (!(YGFloatsEqual(
3169 .measuredDimensions[YGDimensionWidth]) &&
3173 .measuredDimensions[YGDimensionHeight]))) {
3174 YGLayoutNodeInternal(child,
3178 YGMeasureModeExactly,
3179 YGMeasureModeExactly,
3180 availableInnerWidth,
3181 availableInnerHeight,
3183 "multiline-stretch",
3189 case YGAlignBaseline: {
3190 child->setLayoutPosition(
3191 currentLead + maxAscentForCurrentLine - YGBaseline(child) +
3192 YGUnwrapFloatOptional(child->getLeadingPosition(
3193 YGFlexDirectionColumn, availableInnerCrossDim)),
3199 case YGAlignSpaceBetween:
3200 case YGAlignSpaceAround:
3207 currentLead += lineHeight;
3211 // STEP 9: COMPUTING FINAL DIMENSIONS
3213 node->setLayoutMeasuredDimension(
3217 availableWidth - marginAxisRow,
3222 node->setLayoutMeasuredDimension(
3225 YGFlexDirectionColumn,
3226 availableHeight - marginAxisColumn,
3231 // If the user didn't specify a width or height for the node, set the
3232 // dimensions based on the children.
3233 if (measureModeMainDim == YGMeasureModeUndefined ||
3234 (node->getStyle().overflow != YGOverflowScroll &&
3235 measureModeMainDim == YGMeasureModeAtMost)) {
3236 // Clamp the size to the min/max size, if specified, and make sure it
3237 // doesn't go below the padding and border amount.
3238 node->setLayoutMeasuredDimension(
3240 node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth),
3244 measureModeMainDim == YGMeasureModeAtMost &&
3245 node->getStyle().overflow == YGOverflowScroll) {
3246 node->setLayoutMeasuredDimension(
3249 availableInnerMainDim + paddingAndBorderAxisMain,
3250 YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
3251 node, mainAxis, maxLineMainDim, mainAxisownerSize))),
3252 paddingAndBorderAxisMain),
3256 if (measureModeCrossDim == YGMeasureModeUndefined ||
3257 (node->getStyle().overflow != YGOverflowScroll &&
3258 measureModeCrossDim == YGMeasureModeAtMost)) {
3259 // Clamp the size to the min/max size, if specified, and make sure it
3260 // doesn't go below the padding and border amount.
3262 node->setLayoutMeasuredDimension(
3266 totalLineCrossDim + paddingAndBorderAxisCross,
3272 measureModeCrossDim == YGMeasureModeAtMost &&
3273 node->getStyle().overflow == YGOverflowScroll) {
3274 node->setLayoutMeasuredDimension(
3277 availableInnerCrossDim + paddingAndBorderAxisCross,
3278 YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
3281 totalLineCrossDim + paddingAndBorderAxisCross,
3282 crossAxisownerSize))),
3283 paddingAndBorderAxisCross),
3287 // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse.
3288 if (performLayout && node->getStyle().flexWrap == YGWrapWrapReverse) {
3289 for (uint32_t i = 0; i < childCount; i++) {
3290 const YGNodeRef child = YGNodeGetChild(node, i);
3291 if (child->getStyle().positionType == YGPositionTypeRelative) {
3292 child->setLayoutPosition(
3293 node->getLayout().measuredDimensions[dim[crossAxis]] -
3294 child->getLayout().position[pos[crossAxis]] -
3295 child->getLayout().measuredDimensions[dim[crossAxis]],
3301 if (performLayout) {
3302 // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN
3303 for (auto child : node->getChildren()) {
3304 if (child->getStyle().positionType != YGPositionTypeAbsolute) {
3307 YGNodeAbsoluteLayoutChild(
3310 availableInnerWidth,
3311 isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
3312 availableInnerHeight,
3317 // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN
3318 const bool needsMainTrailingPos =
3319 mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse;
3320 const bool needsCrossTrailingPos =
3321 crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse;
3323 // Set trailing position if necessary.
3324 if (needsMainTrailingPos || needsCrossTrailingPos) {
3325 for (uint32_t i = 0; i < childCount; i++) {
3326 const YGNodeRef child = node->getChild(i);
3327 if (child->getStyle().display == YGDisplayNone) {
3330 if (needsMainTrailingPos) {
3331 YGNodeSetChildTrailingPosition(node, child, mainAxis);
3334 if (needsCrossTrailingPos) {
3335 YGNodeSetChildTrailingPosition(node, child, crossAxis);
3342 uint32_t gDepth = 0;
3343 bool gPrintTree = false;
3344 bool gPrintChanges = false;
3345 bool gPrintSkips = false;
3347 static const char *spacer = " ";
3349 static const char *YGSpacer(const unsigned long level) {
3350 const size_t spacerLen = strlen(spacer);
3351 if (level > spacerLen) {
3354 return &spacer[spacerLen - level];
3358 static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) {
3359 const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"};
3360 const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED",
3365 if (mode >= YGMeasureModeCount) {
3369 return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode];
3372 static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode,
3374 float lastComputedSize) {
3375 return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize);
3378 static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode,
3380 YGMeasureMode lastSizeMode,
3381 float lastComputedSize) {
3382 return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined &&
3383 (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize));
3386 static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode,
3388 YGMeasureMode lastSizeMode,
3390 float lastComputedSize) {
3391 return lastSizeMode == YGMeasureModeAtMost &&
3392 sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) &&
3393 !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) &&
3395 (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize));
3398 float YGRoundValueToPixelGrid(const float value,
3399 const float pointScaleFactor,
3400 const bool forceCeil,
3401 const bool forceFloor) {
3402 float scaledValue = value * pointScaleFactor;
3403 float fractial = fmodf(scaledValue, 1.0f);
3404 if (YGFloatsEqual(fractial, 0)) {
3405 // First we check if the value is already rounded
3406 scaledValue = scaledValue - fractial;
3407 } else if (YGFloatsEqual(fractial, 1.0f)) {
3408 scaledValue = scaledValue - fractial + 1.0f;
3409 } else if (forceCeil) {
3410 // Next we check if we need to use forced rounding
3411 scaledValue = scaledValue - fractial + 1.0f;
3412 } else if (forceFloor) {
3413 scaledValue = scaledValue - fractial;
3415 // Finally we just round the value
3416 scaledValue = scaledValue - fractial +
3417 (!YGFloatIsUndefined(fractial) &&
3418 (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f))
3422 return (YGFloatIsUndefined(scaledValue) ||
3423 YGFloatIsUndefined(pointScaleFactor))
3425 : scaledValue / pointScaleFactor;
3428 bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode,
3430 const YGMeasureMode heightMode,
3432 const YGMeasureMode lastWidthMode,
3433 const float lastWidth,
3434 const YGMeasureMode lastHeightMode,
3435 const float lastHeight,
3436 const float lastComputedWidth,
3437 const float lastComputedHeight,
3438 const float marginRow,
3439 const float marginColumn,
3440 const YGConfigRef config) {
3441 if ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) ||
3442 (!YGFloatIsUndefined(lastComputedWidth) && lastComputedWidth < 0)) {
3445 bool useRoundedComparison =
3446 config != nullptr && config->pointScaleFactor != 0;
3447 const float effectiveWidth =
3448 useRoundedComparison ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false)
3450 const float effectiveHeight =
3451 useRoundedComparison ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false)
3453 const float effectiveLastWidth =
3454 useRoundedComparison
3455 ? YGRoundValueToPixelGrid(lastWidth, config->pointScaleFactor, false, false)
3457 const float effectiveLastHeight =
3458 useRoundedComparison
3459 ? YGRoundValueToPixelGrid(lastHeight, config->pointScaleFactor, false, false)
3462 const bool hasSameWidthSpec =
3463 lastWidthMode == widthMode && YGFloatsEqual(effectiveLastWidth, effectiveWidth);
3464 const bool hasSameHeightSpec =
3465 lastHeightMode == heightMode && YGFloatsEqual(effectiveLastHeight, effectiveHeight);
3467 const bool widthIsCompatible =
3468 hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode,
3470 lastComputedWidth) ||
3471 YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode,
3474 lastComputedWidth) ||
3475 YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
3476 widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth);
3478 const bool heightIsCompatible =
3479 hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode,
3480 height - marginColumn,
3481 lastComputedHeight) ||
3482 YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode,
3483 height - marginColumn,
3485 lastComputedHeight) ||
3486 YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
3487 heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight);
3489 return widthIsCompatible && heightIsCompatible;
3493 // This is a wrapper around the YGNodelayoutImpl function. It determines
3494 // whether the layout request is redundant and can be skipped.
3497 // Input parameters are the same as YGNodelayoutImpl (see above)
3498 // Return parameter is true if layout was performed, false if skipped
3500 bool YGLayoutNodeInternal(const YGNodeRef node,
3501 const float availableWidth,
3502 const float availableHeight,
3503 const YGDirection ownerDirection,
3504 const YGMeasureMode widthMeasureMode,
3505 const YGMeasureMode heightMeasureMode,
3506 const float ownerWidth,
3507 const float ownerHeight,
3508 const bool performLayout,
3510 const YGConfigRef config) {
3511 YGLayout* layout = &node->getLayout();
3515 const bool needToVisitNode =
3516 (node->isDirty() && layout->generationCount != gCurrentGenerationCount) ||
3517 layout->lastOwnerDirection != ownerDirection;
3519 if (needToVisitNode) {
3520 // Invalidate the cached results.
3521 layout->nextCachedMeasurementsIndex = 0;
3522 layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1;
3523 layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1;
3524 layout->cachedLayout.computedWidth = -1;
3525 layout->cachedLayout.computedHeight = -1;
3528 YGCachedMeasurement* cachedResults = nullptr;
3530 // Determine whether the results are already cached. We maintain a separate
3531 // cache for layouts and measurements. A layout operation modifies the
3533 // and dimensions for nodes in the subtree. The algorithm assumes that each
3535 // gets layed out a maximum of one time per tree layout, but multiple
3537 // may be required to resolve all of the flex dimensions.
3538 // We handle nodes with measure functions specially here because they are the
3540 // expensive to measure, so it's worth avoiding redundant measurements if at
3542 if (node->getMeasure() != nullptr) {
3543 const float marginAxisRow = YGUnwrapFloatOptional(
3544 node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
3545 const float marginAxisColumn = YGUnwrapFloatOptional(
3546 node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
3548 // First, try to use the layout cache.
3549 if (YGNodeCanUseCachedMeasurement(widthMeasureMode,
3553 layout->cachedLayout.widthMeasureMode,
3554 layout->cachedLayout.availableWidth,
3555 layout->cachedLayout.heightMeasureMode,
3556 layout->cachedLayout.availableHeight,
3557 layout->cachedLayout.computedWidth,
3558 layout->cachedLayout.computedHeight,
3562 cachedResults = &layout->cachedLayout;
3564 // Try to use the measurement cache.
3565 for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
3566 if (YGNodeCanUseCachedMeasurement(widthMeasureMode,
3570 layout->cachedMeasurements[i].widthMeasureMode,
3571 layout->cachedMeasurements[i].availableWidth,
3572 layout->cachedMeasurements[i].heightMeasureMode,
3573 layout->cachedMeasurements[i].availableHeight,
3574 layout->cachedMeasurements[i].computedWidth,
3575 layout->cachedMeasurements[i].computedHeight,
3579 cachedResults = &layout->cachedMeasurements[i];
3584 } else if (performLayout) {
3585 if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&
3586 YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&
3587 layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
3588 layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
3589 cachedResults = &layout->cachedLayout;
3592 for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
3593 if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) &&
3594 YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) &&
3595 layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
3596 layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) {
3597 cachedResults = &layout->cachedMeasurements[i];
3603 if (!needToVisitNode && cachedResults != nullptr) {
3604 layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;
3605 layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight;
3607 if (gPrintChanges && gPrintSkips) {
3608 YGLog(node, YGLogLevelVerbose, "%s%d.{[skipped] ", YGSpacer(gDepth), gDepth);
3609 if (node->getPrintFunc() != nullptr) {
3610 node->getPrintFunc()(node);
3615 "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
3616 YGMeasureModeName(widthMeasureMode, performLayout),
3617 YGMeasureModeName(heightMeasureMode, performLayout),
3620 cachedResults->computedWidth,
3621 cachedResults->computedHeight,
3625 if (gPrintChanges) {
3632 needToVisitNode ? "*" : "");
3633 if (node->getPrintFunc() != nullptr) {
3634 node->getPrintFunc()(node);
3639 "wm: %s, hm: %s, aw: %f ah: %f %s\n",
3640 YGMeasureModeName(widthMeasureMode, performLayout),
3641 YGMeasureModeName(heightMeasureMode, performLayout),
3647 YGNodelayoutImpl(node,
3658 if (gPrintChanges) {
3665 needToVisitNode ? "*" : "");
3666 if (node->getPrintFunc() != nullptr) {
3667 node->getPrintFunc()(node);
3672 "wm: %s, hm: %s, d: (%f, %f) %s\n",
3673 YGMeasureModeName(widthMeasureMode, performLayout),
3674 YGMeasureModeName(heightMeasureMode, performLayout),
3675 layout->measuredDimensions[YGDimensionWidth],
3676 layout->measuredDimensions[YGDimensionHeight],
3680 layout->lastOwnerDirection = ownerDirection;
3682 if (cachedResults == nullptr) {
3683 if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) {
3684 if (gPrintChanges) {
3685 YGLog(node, YGLogLevelVerbose, "Out of cache entries!\n");
3687 layout->nextCachedMeasurementsIndex = 0;
3690 YGCachedMeasurement *newCacheEntry;
3691 if (performLayout) {
3692 // Use the single layout cache entry.
3693 newCacheEntry = &layout->cachedLayout;
3695 // Allocate a new measurement cache entry.
3696 newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
3697 layout->nextCachedMeasurementsIndex++;
3700 newCacheEntry->availableWidth = availableWidth;
3701 newCacheEntry->availableHeight = availableHeight;
3702 newCacheEntry->widthMeasureMode = widthMeasureMode;
3703 newCacheEntry->heightMeasureMode = heightMeasureMode;
3704 newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth];
3705 newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight];
3709 if (performLayout) {
3710 node->setLayoutDimension(
3711 node->getLayout().measuredDimensions[YGDimensionWidth],
3713 node->setLayoutDimension(
3714 node->getLayout().measuredDimensions[YGDimensionHeight],
3717 node->setHasNewLayout(true);
3718 node->setDirty(false);
3722 layout->generationCount = gCurrentGenerationCount;
3723 return (needToVisitNode || cachedResults == nullptr);
3726 void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) {
3727 YGAssertWithConfig(config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero");
3729 // We store points for Pixel as we will use it for rounding
3730 if (pixelsInPoint == 0.0f) {
3731 // Zero is used to skip rounding
3732 config->pointScaleFactor = 0.0f;
3734 config->pointScaleFactor = pixelsInPoint;
3738 static void YGRoundToPixelGrid(const YGNodeRef node,
3739 const float pointScaleFactor,
3740 const float absoluteLeft,
3741 const float absoluteTop) {
3742 if (pointScaleFactor == 0.0f) {
3746 const float nodeLeft = node->getLayout().position[YGEdgeLeft];
3747 const float nodeTop = node->getLayout().position[YGEdgeTop];
3749 const float nodeWidth = node->getLayout().dimensions[YGDimensionWidth];
3750 const float nodeHeight = node->getLayout().dimensions[YGDimensionHeight];
3752 const float absoluteNodeLeft = absoluteLeft + nodeLeft;
3753 const float absoluteNodeTop = absoluteTop + nodeTop;
3755 const float absoluteNodeRight = absoluteNodeLeft + nodeWidth;
3756 const float absoluteNodeBottom = absoluteNodeTop + nodeHeight;
3758 // If a node has a custom measure function we never want to round down its size as this could
3759 // lead to unwanted text truncation.
3760 const bool textRounding = node->getNodeType() == YGNodeTypeText;
3762 node->setLayoutPosition(
3763 YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
3766 node->setLayoutPosition(
3767 YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
3770 // We multiply dimension by scale factor and if the result is close to the whole number, we don't
3771 // have any fraction
3772 // To verify if the result is close to whole number we want to check both floor and ceil numbers
3773 const bool hasFractionalWidth = !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) &&
3774 !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0);
3775 const bool hasFractionalHeight = !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) &&
3776 !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0);
3778 node->setLayoutDimension(
3779 YGRoundValueToPixelGrid(
3782 (textRounding && hasFractionalWidth),
3783 (textRounding && !hasFractionalWidth)) -
3784 YGRoundValueToPixelGrid(
3785 absoluteNodeLeft, pointScaleFactor, false, textRounding),
3788 node->setLayoutDimension(
3789 YGRoundValueToPixelGrid(
3792 (textRounding && hasFractionalHeight),
3793 (textRounding && !hasFractionalHeight)) -
3794 YGRoundValueToPixelGrid(
3795 absoluteNodeTop, pointScaleFactor, false, textRounding),
3798 const uint32_t childCount = YGNodeGetChildCount(node);
3799 for (uint32_t i = 0; i < childCount; i++) {
3801 YGNodeGetChild(node, i),
3808 void YGNodeCalculateLayout(
3809 const YGNodeRef node,
3810 const float ownerWidth,
3811 const float ownerHeight,
3812 const YGDirection ownerDirection) {
3813 // Increment the generation count. This will force the recursive routine to
3815 // all dirty nodes at least once. Subsequent visits will be skipped if the
3817 // parameters don't change.
3818 gCurrentGenerationCount++;
3819 node->resolveDimension();
3820 float width = YGUndefined;
3821 YGMeasureMode widthMeasureMode = YGMeasureModeUndefined;
3822 if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, ownerWidth)) {
3823 width = YGUnwrapFloatOptional(
3825 node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) +
3826 node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
3827 widthMeasureMode = YGMeasureModeExactly;
3828 } else if (!YGResolveValue(
3829 node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)
3831 width = YGUnwrapFloatOptional(YGResolveValue(
3832 node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth));
3833 widthMeasureMode = YGMeasureModeAtMost;
3836 widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined
3837 : YGMeasureModeExactly;
3840 float height = YGUndefined;
3841 YGMeasureMode heightMeasureMode = YGMeasureModeUndefined;
3842 if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) {
3843 height = YGUnwrapFloatOptional(
3845 node->getResolvedDimension(dim[YGFlexDirectionColumn]),
3847 node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
3848 heightMeasureMode = YGMeasureModeExactly;
3849 } else if (!YGResolveValue(
3850 node->getStyle().maxDimensions[YGDimensionHeight],
3853 height = YGUnwrapFloatOptional(YGResolveValue(node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight));
3854 heightMeasureMode = YGMeasureModeAtMost;
3856 height = ownerHeight;
3857 heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined
3858 : YGMeasureModeExactly;
3860 if (YGLayoutNodeInternal(
3871 node->getConfig())) {
3873 node->getLayout().direction, ownerWidth, ownerHeight, ownerWidth);
3874 YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f);
3880 YGPrintOptionsLayout | YGPrintOptionsChildren |
3881 YGPrintOptionsStyle));
3885 // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we
3886 // aren't sure whether client's of yoga have gotten rid off this flag or not.
3887 // So logging this in YGLayout would help to find out the call sites depending
3888 // on this flag. This check would be removed once we are sure no one is
3889 // dependent on this flag anymore. The flag
3890 // `shouldDiffLayoutWithoutLegacyStretchBehaviour` in YGConfig will help to
3892 if (node->getConfig()->shouldDiffLayoutWithoutLegacyStretchBehaviour &&
3893 node->didUseLegacyFlag()) {
3894 const YGNodeRef originalNode = YGNodeDeepClone(node);
3895 originalNode->resolveDimension();
3896 // Recursively mark nodes as dirty
3897 originalNode->markDirtyAndPropogateDownwards();
3898 gCurrentGenerationCount++;
3899 // Rerun the layout, and calculate the diff
3900 originalNode->setAndPropogateUseLegacyFlag(false);
3901 if (YGLayoutNodeInternal(
3912 originalNode->getConfig())) {
3913 originalNode->setPosition(
3914 originalNode->getLayout().direction,
3920 originalNode->getConfig()->pointScaleFactor,
3924 // Set whether the two layouts are different or not.
3925 node->setLayoutDoesLegacyFlagAffectsLayout(
3926 !originalNode->isLayoutTreeEqualToNode(*node));
3932 YGPrintOptionsLayout | YGPrintOptionsChildren |
3933 YGPrintOptionsStyle));
3936 YGConfigFreeRecursive(originalNode);
3937 YGNodeFreeRecursive(originalNode);
3941 void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) {
3942 if (logger != nullptr) {
3943 config->logger = logger;
3946 config->logger = &YGAndroidLog;
3948 config->logger = &YGDefaultLog;
3953 void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour(
3954 const YGConfigRef config,
3955 const bool shouldDiffLayout) {
3956 config->shouldDiffLayoutWithoutLegacyStretchBehaviour = shouldDiffLayout;
3959 static void YGVLog(const YGConfigRef config,
3960 const YGNodeRef node,
3964 const YGConfigRef logConfig = config != nullptr ? config : YGConfigGetDefault();
3965 logConfig->logger(logConfig, node, level, format, args);
3967 if (level == YGLogLevelFatal) {
3972 void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...) {
3974 va_start(args, format);
3975 YGVLog(config, nullptr, level, format, args);
3979 void YGLog(const YGNodeRef node, YGLogLevel level, const char *format, ...) {
3981 va_start(args, format);
3983 node == nullptr ? nullptr : node->getConfig(), node, level, format, args);
3987 void YGAssert(const bool condition, const char *message) {
3989 YGLog(nullptr, YGLogLevelFatal, "%s\n", message);
3993 void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message) {
3995 YGLog(node, YGLogLevelFatal, "%s\n", message);
3999 void YGAssertWithConfig(const YGConfigRef config, const bool condition, const char *message) {
4001 YGLogWithConfig(config, YGLogLevelFatal, "%s\n", message);
4005 void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config,
4006 const YGExperimentalFeature feature,
4007 const bool enabled) {
4008 config->experimentalFeatures[feature] = enabled;
4011 inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config,
4012 const YGExperimentalFeature feature) {
4013 return config->experimentalFeatures[feature];
4016 void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) {
4017 config->useWebDefaults = enabled;
4020 void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config,
4021 const bool useLegacyStretchBehaviour) {
4022 config->useLegacyStretchBehaviour = useLegacyStretchBehaviour;
4025 bool YGConfigGetUseWebDefaults(const YGConfigRef config) {
4026 return config->useWebDefaults;
4029 void YGConfigSetContext(const YGConfigRef config, void *context) {
4030 config->context = context;
4033 void *YGConfigGetContext(const YGConfigRef config) {
4034 return config->context;
4037 void YGConfigSetCloneNodeFunc(const YGConfigRef config, const YGCloneNodeFunc callback) {
4038 config->cloneNodeCallback = callback;
4041 static void YGTraverseChildrenPreOrder(const YGVector& children, const std::function<void(YGNodeRef node)>& f) {
4042 for (YGNodeRef node : children) {
4044 YGTraverseChildrenPreOrder(node->getChildren(), f);
4048 void YGTraversePreOrder(YGNodeRef const node, std::function<void(YGNodeRef node)>&& f) {
4053 YGTraverseChildrenPreOrder(node->getChildren(), f);