* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <float.h>
#include <string.h>
#include <algorithm>
#include <atomic>
#include <memory>
#include <yoga/Yoga.h>
#include "log.h"
#include "Utils.h"
#include "YGNode.h"
#include "YGNodePrint.h"
#include "Yoga-internal.h"
#include "event/event.h"
using namespace facebook::yoga;
using detail::Log;
#ifdef ANDROID
static int YGAndroidLog(
const YGConfigRef config,
const YGNodeRef node,
YGLogLevel level,
const char* format,
va_list args);
#else
static int YGDefaultLog(
const YGConfigRef config,
const YGNodeRef node,
YGLogLevel level,
const char* format,
va_list args);
#endif
#ifdef ANDROID
#include <android/log.h>
static int YGAndroidLog(
const YGConfigRef ,
const YGNodeRef ,
YGLogLevel level,
const char* format,
va_list args) {
int androidLevel = YGLogLevelDebug;
switch (level) {
case YGLogLevelFatal:
androidLevel = ANDROID_LOG_FATAL;
break;
case YGLogLevelError:
androidLevel = ANDROID_LOG_ERROR;
break;
case YGLogLevelWarn:
androidLevel = ANDROID_LOG_WARN;
break;
case YGLogLevelInfo:
androidLevel = ANDROID_LOG_INFO;
break;
case YGLogLevelDebug:
androidLevel = ANDROID_LOG_DEBUG;
break;
case YGLogLevelVerbose:
androidLevel = ANDROID_LOG_VERBOSE;
break;
}
const int result = __android_log_vprint(androidLevel, "yoga", format, args);
return result;
}
#else
#define YG_UNUSED(x) (void) (x);
static int YGDefaultLog(
const YGConfigRef config,
const YGNodeRef node,
YGLogLevel level,
const char* format,
va_list args) {
YG_UNUSED(config);
YG_UNUSED(node);
switch (level) {
case YGLogLevelError:
case YGLogLevelFatal:
return vfprintf(stderr, format, args);
case YGLogLevelWarn:
case YGLogLevelInfo:
case YGLogLevelDebug:
case YGLogLevelVerbose:
default:
return vprintf(format, args);
}
}
#undef YG_UNUSED
#endif
static inline bool YGDoubleIsUndefined(const double value) {
return facebook::yoga::isUndefined(value);
}
YOGA_EXPORT bool YGFloatIsUndefined(const float value) {
return facebook::yoga::isUndefined(value);
}
YOGA_EXPORT void* YGNodeGetContext(YGNodeRef node) {
return node->getContext();
}
YOGA_EXPORT void YGNodeSetContext(YGNodeRef node, void* context) {
return node->setContext(context);
}
YOGA_EXPORT YGConfigRef YGNodeGetConfig(YGNodeRef node) {
return node->getConfig();
}
YOGA_EXPORT void YGNodeSetConfig(YGNodeRef node, YGConfigRef config) {
node->setConfig(config);
}
YOGA_EXPORT bool YGNodeHasMeasureFunc(YGNodeRef node) {
return node->hasMeasureFunc();
}
YOGA_EXPORT void YGNodeSetMeasureFunc(
YGNodeRef node,
YGMeasureFunc measureFunc) {
node->setMeasureFunc(measureFunc);
}
YOGA_EXPORT bool YGNodeHasBaselineFunc(YGNodeRef node) {
return node->hasBaselineFunc();
}
YOGA_EXPORT void YGNodeSetBaselineFunc(
YGNodeRef node,
YGBaselineFunc baselineFunc) {
node->setBaselineFunc(baselineFunc);
}
YOGA_EXPORT YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node) {
return node->getDirtied();
}
YOGA_EXPORT void YGNodeSetDirtiedFunc(
YGNodeRef node,
YGDirtiedFunc dirtiedFunc) {
node->setDirtiedFunc(dirtiedFunc);
}
YOGA_EXPORT void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
node->setPrintFunc(printFunc);
}
YOGA_EXPORT bool YGNodeGetHasNewLayout(YGNodeRef node) {
return node->getHasNewLayout();
}
YOGA_EXPORT void YGConfigSetPrintTreeFlag(YGConfigRef config, bool enabled) {
config->setShouldPrintTree(enabled);
}
YOGA_EXPORT void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
node->setHasNewLayout(hasNewLayout);
}
YOGA_EXPORT YGNodeType YGNodeGetNodeType(YGNodeRef node) {
return node->getNodeType();
}
YOGA_EXPORT void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
return node->setNodeType(nodeType);
}
YOGA_EXPORT bool YGNodeIsDirty(YGNodeRef node) {
return node->isDirty();
}
YOGA_EXPORT void YGNodeMarkDirtyAndPropagateToDescendants(
const YGNodeRef node) {
return node->markDirtyAndPropagateDownwards();
}
int32_t gConfigInstanceCount = 0;
YOGA_EXPORT WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
const YGNodeRef node = new YGNode{config};
YGAssert(config != nullptr, "Tried to construct YGNode with null config");
YGAssertWithConfig(
config, node != nullptr, "Could not allocate memory for node");
Event::publish<Event::NodeAllocation>(node, {config});
return node;
}
YOGA_EXPORT YGConfigRef YGConfigGetDefault() {
static YGConfigRef defaultConfig = YGConfigNew();
return defaultConfig;
}
YOGA_EXPORT YGNodeRef YGNodeNew(void) {
return YGNodeNewWithConfig(YGConfigGetDefault());
}
YOGA_EXPORT YGNodeRef YGNodeClone(YGNodeRef oldNode) {
YGNodeRef node = new YGNode(*oldNode);
YGAssertWithConfig(
oldNode->getConfig(),
node != nullptr,
"Could not allocate memory for node");
Event::publish<Event::NodeAllocation>(node, {node->getConfig()});
node->setOwner(nullptr);
return node;
}
YOGA_EXPORT void YGNodeFree(const YGNodeRef node) {
if (YGNodeRef owner = node->getOwner()) {
owner->removeChild(node);
node->setOwner(nullptr);
}
const uint32_t childCount = YGNodeGetChildCount(node);
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef child = YGNodeGetChild(node, i);
child->setOwner(nullptr);
}
node->clearChildren();
YGNodeDeallocate(node);
}
YOGA_EXPORT void YGNodeDeallocate(const YGNodeRef node) {
Event::publish<Event::NodeDeallocation>(node, {node->getConfig()});
delete node;
}
YOGA_EXPORT void YGNodeFreeRecursiveWithCleanupFunc(
const YGNodeRef root,
YGNodeCleanupFunc cleanup) {
uint32_t skipped = 0;
while (YGNodeGetChildCount(root) > skipped) {
const YGNodeRef child = YGNodeGetChild(root, skipped);
if (child->getOwner() != root) {
skipped += 1;
} else {
YGNodeRemoveChild(root, child);
YGNodeFreeRecursive(child);
}
}
if (cleanup != nullptr) {
cleanup(root);
}
YGNodeFree(root);
}
YOGA_EXPORT void YGNodeFreeRecursive(const YGNodeRef root) {
return YGNodeFreeRecursiveWithCleanupFunc(root, nullptr);
}
YOGA_EXPORT void YGNodeReset(YGNodeRef node) {
node->reset();
}
YOGA_EXPORT int32_t YGConfigGetInstanceCount(void) {
return gConfigInstanceCount;
}
YOGA_EXPORT YGConfigRef YGConfigNew(void) {
#ifdef ANDROID
const YGConfigRef config = new YGConfig(YGAndroidLog);
#else
const YGConfigRef config = new YGConfig(YGDefaultLog);
#endif
gConfigInstanceCount++;
return config;
}
YOGA_EXPORT void YGConfigFree(const YGConfigRef config) {
delete config;
gConfigInstanceCount--;
}
void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) {
memcpy(dest, src, sizeof(YGConfig));
}
YOGA_EXPORT void YGNodeSetIsReferenceBaseline(
YGNodeRef node,
bool isReferenceBaseline) {
if (node->isReferenceBaseline() != isReferenceBaseline) {
node->setIsReferenceBaseline(isReferenceBaseline);
node->markDirtyAndPropagate();
}
}
YOGA_EXPORT bool YGNodeIsReferenceBaseline(YGNodeRef node) {
return node->isReferenceBaseline();
}
YOGA_EXPORT void YGNodeInsertChild(
const YGNodeRef owner,
const YGNodeRef child,
const uint32_t index) {
YGAssertWithNode(
owner,
child->getOwner() == nullptr,
"Child already has a owner, it must be removed first.");
YGAssertWithNode(
owner,
!owner->hasMeasureFunc(),
"Cannot add child: Nodes with measure functions cannot have children.");
owner->insertChild(child, index);
child->setOwner(owner);
owner->markDirtyAndPropagate();
}
YOGA_EXPORT void YGNodeSwapChild(
const YGNodeRef owner,
const YGNodeRef child,
const uint32_t index) {
owner->replaceChild(child, index);
child->setOwner(owner);
}
YOGA_EXPORT void YGNodeRemoveChild(
const YGNodeRef owner,
const YGNodeRef excludedChild) {
if (YGNodeGetChildCount(owner) == 0) {
return;
}
auto childOwner = excludedChild->getOwner();
if (owner->removeChild(excludedChild)) {
if (owner == childOwner) {
excludedChild->setLayout({});
excludedChild->setOwner(nullptr);
}
owner->markDirtyAndPropagate();
}
}
YOGA_EXPORT void YGNodeRemoveAllChildren(const YGNodeRef owner) {
const uint32_t childCount = YGNodeGetChildCount(owner);
if (childCount == 0) {
return;
}
const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
if (firstChild->getOwner() == owner) {
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef oldChild = YGNodeGetChild(owner, i);
oldChild->setLayout(YGNode().getLayout());
oldChild->setOwner(nullptr);
}
owner->clearChildren();
owner->markDirtyAndPropagate();
return;
}
owner->setChildren(YGVector());
owner->markDirtyAndPropagate();
}
YOGA_EXPORT void YGNodeSetChildren(
const YGNodeRef owner,
const YGNodeRef* children,
const uint32_t count) {
if (!owner) {
return;
}
const YGVector childrenVector = {children, children + count};
if (childrenVector.size() == 0) {
if (YGNodeGetChildCount(owner) > 0) {
for (YGNodeRef const child : owner->getChildren()) {
child->setLayout(YGLayout());
child->setOwner(nullptr);
}
owner->setChildren(YGVector());
owner->markDirtyAndPropagate();
}
} else {
if (YGNodeGetChildCount(owner) > 0) {
for (YGNodeRef const oldChild : owner->getChildren()) {
if (std::find(childrenVector.begin(), childrenVector.end(), oldChild) ==
childrenVector.end()) {
oldChild->setLayout(YGLayout());
oldChild->setOwner(nullptr);
}
}
}
owner->setChildren(childrenVector);
for (YGNodeRef child : childrenVector) {
child->setOwner(owner);
}
owner->markDirtyAndPropagate();
}
}
YOGA_EXPORT YGNodeRef
YGNodeGetChild(const YGNodeRef node, const uint32_t index) {
if (index < node->getChildren().size()) {
return node->getChild(index);
}
return nullptr;
}
YOGA_EXPORT uint32_t YGNodeGetChildCount(const YGNodeRef node) {
return static_cast<uint32_t>(node->getChildren().size());
}
YOGA_EXPORT YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
return node->getOwner();
}
YOGA_EXPORT YGNodeRef YGNodeGetParent(const YGNodeRef node) {
return node->getOwner();
}
YOGA_EXPORT void YGNodeMarkDirty(const YGNodeRef node) {
YGAssertWithNode(
node,
node->hasMeasureFunc(),
"Only leaf nodes with custom measure functions "
"should manually mark themselves as dirty");
node->markDirtyAndPropagate();
}
YOGA_EXPORT void YGNodeCopyStyle(
const YGNodeRef dstNode,
const YGNodeRef srcNode) {
if (!(dstNode->getStyle() == srcNode->getStyle())) {
dstNode->setStyle(srcNode->getStyle());
dstNode->markDirtyAndPropagate();
}
}
YOGA_EXPORT float YGNodeStyleGetFlexGrow(const YGNodeConstRef node) {
return node->getStyle().flexGrow().isUndefined()
? kDefaultFlexGrow
: node->getStyle().flexGrow().unwrap();
}
YOGA_EXPORT float YGNodeStyleGetFlexShrink(const YGNodeConstRef node) {
return node->getStyle().flexShrink().isUndefined()
? (node->getConfig()->useWebDefaults() ? kWebDefaultFlexShrink
: kDefaultFlexShrink)
: node->getStyle().flexShrink().unwrap();
}
namespace {
template <typename T, typename NeedsUpdate, typename Update>
void updateStyle(
YGNode* node,
T value,
NeedsUpdate&& needsUpdate,
Update&& update) {
if (needsUpdate(node->getStyle(), value)) {
update(node->getStyle(), value);
node->markDirtyAndPropagate();
}
}
template <typename Ref, typename T>
void updateStyle(YGNode* node, Ref (YGStyle::*prop)(), T value) {
updateStyle(
node,
value,
[prop](YGStyle& s, T x) { return (s.*prop)() != x; },
[prop](YGStyle& s, T x) { (s.*prop)() = x; });
}
template <typename Ref, typename Idx>
void updateIndexedStyleProp(
YGNode* node,
Ref (YGStyle::*prop)(),
Idx idx,
detail::CompactValue value) {
using detail::CompactValue;
updateStyle(
node,
value,
[idx, prop](YGStyle& s, CompactValue x) { return (s.*prop)()[idx] != x; },
[idx, prop](YGStyle& s, CompactValue x) { (s.*prop)()[idx] = x; });
}
}
#define MSVC_HINT(PROP) decltype(YGStyle{}.PROP())
YOGA_EXPORT void YGNodeStyleSetDirection(
const YGNodeRef node,
const YGDirection value) {
updateStyle<MSVC_HINT(direction)>(node, &YGStyle::direction, value);
}
YOGA_EXPORT YGDirection YGNodeStyleGetDirection(const YGNodeConstRef node) {
return node->getStyle().direction();
}
YOGA_EXPORT void YGNodeStyleSetFlexDirection(
const YGNodeRef node,
const YGFlexDirection flexDirection) {
updateStyle<MSVC_HINT(flexDirection)>(
node, &YGStyle::flexDirection, flexDirection);
}
YOGA_EXPORT YGFlexDirection
YGNodeStyleGetFlexDirection(const YGNodeConstRef node) {
return node->getStyle().flexDirection();
}
YOGA_EXPORT void YGNodeStyleSetJustifyContent(
const YGNodeRef node,
const YGJustify justifyContent) {
updateStyle<MSVC_HINT(justifyContent)>(
node, &YGStyle::justifyContent, justifyContent);
}
YOGA_EXPORT YGJustify YGNodeStyleGetJustifyContent(const YGNodeConstRef node) {
return node->getStyle().justifyContent();
}
YOGA_EXPORT void YGNodeStyleSetAlignContent(
const YGNodeRef node,
const YGAlign alignContent) {
updateStyle<MSVC_HINT(alignContent)>(
node, &YGStyle::alignContent, alignContent);
}
YOGA_EXPORT YGAlign YGNodeStyleGetAlignContent(const YGNodeConstRef node) {
return node->getStyle().alignContent();
}
YOGA_EXPORT void YGNodeStyleSetAlignItems(
const YGNodeRef node,
const YGAlign alignItems) {
updateStyle<MSVC_HINT(alignItems)>(node, &YGStyle::alignItems, alignItems);
}
YOGA_EXPORT YGAlign YGNodeStyleGetAlignItems(const YGNodeConstRef node) {
return node->getStyle().alignItems();
}
YOGA_EXPORT void YGNodeStyleSetAlignSelf(
const YGNodeRef node,
const YGAlign alignSelf) {
updateStyle<MSVC_HINT(alignSelf)>(node, &YGStyle::alignSelf, alignSelf);
}
YOGA_EXPORT YGAlign YGNodeStyleGetAlignSelf(const YGNodeConstRef node) {
return node->getStyle().alignSelf();
}
YOGA_EXPORT void YGNodeStyleSetPositionType(
const YGNodeRef node,
const YGPositionType positionType) {
updateStyle<MSVC_HINT(positionType)>(
node, &YGStyle::positionType, positionType);
}
YOGA_EXPORT YGPositionType
YGNodeStyleGetPositionType(const YGNodeConstRef node) {
return node->getStyle().positionType();
}
YOGA_EXPORT void YGNodeStyleSetFlexWrap(
const YGNodeRef node,
const YGWrap flexWrap) {
updateStyle<MSVC_HINT(flexWrap)>(node, &YGStyle::flexWrap, flexWrap);
}
YOGA_EXPORT YGWrap YGNodeStyleGetFlexWrap(const YGNodeConstRef node) {
return node->getStyle().flexWrap();
}
YOGA_EXPORT void YGNodeStyleSetOverflow(
const YGNodeRef node,
const YGOverflow overflow) {
updateStyle<MSVC_HINT(overflow)>(node, &YGStyle::overflow, overflow);
}
YOGA_EXPORT YGOverflow YGNodeStyleGetOverflow(const YGNodeConstRef node) {
return node->getStyle().overflow();
}
YOGA_EXPORT void YGNodeStyleSetDisplay(
const YGNodeRef node,
const YGDisplay display) {
updateStyle<MSVC_HINT(display)>(node, &YGStyle::display, display);
}
YOGA_EXPORT YGDisplay YGNodeStyleGetDisplay(const YGNodeConstRef node) {
return node->getStyle().display();
}
YOGA_EXPORT void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) {
updateStyle<MSVC_HINT(flex)>(node, &YGStyle::flex, YGFloatOptional{flex});
}
YOGA_EXPORT float YGNodeStyleGetFlex(const YGNodeConstRef node) {
return node->getStyle().flex().isUndefined()
? YGUndefined
: node->getStyle().flex().unwrap();
}
YOGA_EXPORT void YGNodeStyleSetFlexGrow(
const YGNodeRef node,
const float flexGrow) {
updateStyle<MSVC_HINT(flexGrow)>(
node, &YGStyle::flexGrow, YGFloatOptional{flexGrow});
}
YOGA_EXPORT void YGNodeStyleSetFlexShrink(
const YGNodeRef node,
const float flexShrink) {
updateStyle<MSVC_HINT(flexShrink)>(
node, &YGStyle::flexShrink, YGFloatOptional{flexShrink});
}
YOGA_EXPORT YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) {
YGValue flexBasis = node->getStyle().flexBasis();
if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) {
flexBasis.value = YGUndefined;
}
return flexBasis;
}
YOGA_EXPORT void YGNodeStyleSetFlexBasis(
const YGNodeRef node,
const float flexBasis) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(flexBasis);
updateStyle<MSVC_HINT(flexBasis)>(node, &YGStyle::flexBasis, value);
}
YOGA_EXPORT void YGNodeStyleSetFlexBasisPercent(
const YGNodeRef node,
const float flexBasisPercent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(flexBasisPercent);
updateStyle<MSVC_HINT(flexBasis)>(node, &YGStyle::flexBasis, value);
}
YOGA_EXPORT void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) {
updateStyle<MSVC_HINT(flexBasis)>(
node, &YGStyle::flexBasis, detail::CompactValue::ofAuto());
}
YOGA_EXPORT void YGNodeStyleSetPosition(
YGNodeRef node,
YGEdge edge,
float points) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(points);
updateIndexedStyleProp<MSVC_HINT(position)>(
node, &YGStyle::position, edge, value);
}
YOGA_EXPORT void YGNodeStyleSetPositionPercent(
YGNodeRef node,
YGEdge edge,
float percent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(percent);
updateIndexedStyleProp<MSVC_HINT(position)>(
node, &YGStyle::position, edge, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge) {
return node->getStyle().position()[edge];
}
YOGA_EXPORT void YGNodeStyleSetMargin(
YGNodeRef node,
YGEdge edge,
float points) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(points);
updateIndexedStyleProp<MSVC_HINT(margin)>(
node, &YGStyle::margin, edge, value);
}
YOGA_EXPORT void YGNodeStyleSetMarginPercent(
YGNodeRef node,
YGEdge edge,
float percent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(percent);
updateIndexedStyleProp<MSVC_HINT(margin)>(
node, &YGStyle::margin, edge, value);
}
YOGA_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge) {
updateIndexedStyleProp<MSVC_HINT(margin)>(
node, &YGStyle::margin, edge, detail::CompactValue::ofAuto());
}
YOGA_EXPORT YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge) {
return node->getStyle().margin()[edge];
}
YOGA_EXPORT void YGNodeStyleSetPadding(
YGNodeRef node,
YGEdge edge,
float points) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(points);
updateIndexedStyleProp<MSVC_HINT(padding)>(
node, &YGStyle::padding, edge, value);
}
YOGA_EXPORT void YGNodeStyleSetPaddingPercent(
YGNodeRef node,
YGEdge edge,
float percent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(percent);
updateIndexedStyleProp<MSVC_HINT(padding)>(
node, &YGStyle::padding, edge, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) {
return node->getStyle().padding()[edge];
}
YOGA_EXPORT void YGNodeStyleSetBorder(
const YGNodeRef node,
const YGEdge edge,
const float border) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(border);
updateIndexedStyleProp<MSVC_HINT(border)>(
node, &YGStyle::border, edge, value);
}
YOGA_EXPORT float YGNodeStyleGetBorder(
const YGNodeConstRef node,
const YGEdge edge) {
auto border = node->getStyle().border()[edge];
if (border.isUndefined() || border.isAuto()) {
return YGUndefined;
}
return static_cast<YGValue>(border).value;
}
YOGA_EXPORT void YGNodeStyleSetGap(
const YGNodeRef node,
const YGGutter gutter,
const float gapLength) {
auto length = detail::CompactValue::ofMaybe<YGUnitPoint>(gapLength);
updateIndexedStyleProp<MSVC_HINT(gap)>(node, &YGStyle::gap, gutter, length);
}
YOGA_EXPORT float YGNodeStyleGetGap(
const YGNodeConstRef node,
const YGGutter gutter) {
auto gapLength = node->getStyle().gap()[gutter];
if (gapLength.isUndefined() || gapLength.isAuto()) {
return YGUndefined;
}
return static_cast<YGValue>(gapLength).value;
}
YOGA_EXPORT float YGNodeStyleGetAspectRatio(const YGNodeConstRef node) {
const YGFloatOptional op = node->getStyle().aspectRatio();
return op.isUndefined() ? YGUndefined : op.unwrap();
}
YOGA_EXPORT void YGNodeStyleSetAspectRatio(
const YGNodeRef node,
const float aspectRatio) {
updateStyle<MSVC_HINT(aspectRatio)>(
node, &YGStyle::aspectRatio, YGFloatOptional{aspectRatio});
}
YOGA_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float points) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(points);
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node, &YGStyle::dimensions, YGDimensionWidth, value);
}
YOGA_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float percent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(percent);
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node, &YGStyle::dimensions, YGDimensionWidth, value);
}
YOGA_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node) {
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node,
&YGStyle::dimensions,
YGDimensionWidth,
detail::CompactValue::ofAuto());
}
YOGA_EXPORT YGValue YGNodeStyleGetWidth(YGNodeConstRef node) {
return node->getStyle().dimensions()[YGDimensionWidth];
}
YOGA_EXPORT void YGNodeStyleSetHeight(YGNodeRef node, float points) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(points);
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node, &YGStyle::dimensions, YGDimensionHeight, value);
}
YOGA_EXPORT void YGNodeStyleSetHeightPercent(YGNodeRef node, float percent) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(percent);
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node, &YGStyle::dimensions, YGDimensionHeight, value);
}
YOGA_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node) {
updateIndexedStyleProp<MSVC_HINT(dimensions)>(
node,
&YGStyle::dimensions,
YGDimensionHeight,
detail::CompactValue::ofAuto());
}
YOGA_EXPORT YGValue YGNodeStyleGetHeight(YGNodeConstRef node) {
return node->getStyle().dimensions()[YGDimensionHeight];
}
YOGA_EXPORT void YGNodeStyleSetMinWidth(
const YGNodeRef node,
const float minWidth) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(minWidth);
updateIndexedStyleProp<MSVC_HINT(minDimensions)>(
node, &YGStyle::minDimensions, YGDimensionWidth, value);
}
YOGA_EXPORT void YGNodeStyleSetMinWidthPercent(
const YGNodeRef node,
const float minWidth) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(minWidth);
updateIndexedStyleProp<MSVC_HINT(minDimensions)>(
node, &YGStyle::minDimensions, YGDimensionWidth, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetMinWidth(const YGNodeConstRef node) {
return node->getStyle().minDimensions()[YGDimensionWidth];
}
YOGA_EXPORT void YGNodeStyleSetMinHeight(
const YGNodeRef node,
const float minHeight) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(minHeight);
updateIndexedStyleProp<MSVC_HINT(minDimensions)>(
node, &YGStyle::minDimensions, YGDimensionHeight, value);
}
YOGA_EXPORT void YGNodeStyleSetMinHeightPercent(
const YGNodeRef node,
const float minHeight) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(minHeight);
updateIndexedStyleProp<MSVC_HINT(minDimensions)>(
node, &YGStyle::minDimensions, YGDimensionHeight, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetMinHeight(const YGNodeConstRef node) {
return node->getStyle().minDimensions()[YGDimensionHeight];
}
YOGA_EXPORT void YGNodeStyleSetMaxWidth(
const YGNodeRef node,
const float maxWidth) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(maxWidth);
updateIndexedStyleProp<MSVC_HINT(maxDimensions)>(
node, &YGStyle::maxDimensions, YGDimensionWidth, value);
}
YOGA_EXPORT void YGNodeStyleSetMaxWidthPercent(
const YGNodeRef node,
const float maxWidth) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(maxWidth);
updateIndexedStyleProp<MSVC_HINT(maxDimensions)>(
node, &YGStyle::maxDimensions, YGDimensionWidth, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetMaxWidth(const YGNodeConstRef node) {
return node->getStyle().maxDimensions()[YGDimensionWidth];
}
YOGA_EXPORT void YGNodeStyleSetMaxHeight(
const YGNodeRef node,
const float maxHeight) {
auto value = detail::CompactValue::ofMaybe<YGUnitPoint>(maxHeight);
updateIndexedStyleProp<MSVC_HINT(maxDimensions)>(
node, &YGStyle::maxDimensions, YGDimensionHeight, value);
}
YOGA_EXPORT void YGNodeStyleSetMaxHeightPercent(
const YGNodeRef node,
const float maxHeight) {
auto value = detail::CompactValue::ofMaybe<YGUnitPercent>(maxHeight);
updateIndexedStyleProp<MSVC_HINT(maxDimensions)>(
node, &YGStyle::maxDimensions, YGDimensionHeight, value);
}
YOGA_EXPORT YGValue YGNodeStyleGetMaxHeight(const YGNodeConstRef node) {
return node->getStyle().maxDimensions()[YGDimensionHeight];
}
#define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \
YOGA_EXPORT type YGNodeLayoutGet##name(const YGNodeRef node) { \
return node->getLayout().instanceName; \
}
#define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \
YOGA_EXPORT type YGNodeLayoutGet##name( \
const YGNodeRef node, const YGEdge edge) { \
YGAssertWithNode( \
node, \
edge <= YGEdgeEnd, \
"Cannot get layout properties of multi-edge shorthands"); \
\
if (edge == YGEdgeStart) { \
if (node->getLayout().direction() == YGDirectionRTL) { \
return node->getLayout().instanceName[YGEdgeRight]; \
} else { \
return node->getLayout().instanceName[YGEdgeLeft]; \
} \
} \
\
if (edge == YGEdgeEnd) { \
if (node->getLayout().direction() == YGDirectionRTL) { \
return node->getLayout().instanceName[YGEdgeLeft]; \
} else { \
return node->getLayout().instanceName[YGEdgeRight]; \
} \
} \
\
return node->getLayout().instanceName[edge]; \
}
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft])
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop])
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight])
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom])
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth])
YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight])
YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction())
YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow())
YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin)
YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border)
YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding)
std::atomic<uint32_t> gCurrentGenerationCount(0);
bool YGLayoutNodeInternal(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGDirection ownerDirection,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight,
const bool performLayout,
const LayoutPassReason reason,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount);
#ifdef DEBUG
static void YGNodePrintInternal(
const YGNodeRef node,
const YGPrintOptions options) {
std::string str;
facebook::yoga::YGNodeToString(str, node, options, 0);
Log::log(node, YGLogLevelDebug, nullptr, str.c_str());
}
YOGA_EXPORT void YGNodePrint(
const YGNodeRef node,
const YGPrintOptions options) {
YGNodePrintInternal(node, options);
}
#endif
const std::array<YGEdge, 4> leading = {
{YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}};
const std::array<YGEdge, 4> trailing = {
{YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}};
static const std::array<YGEdge, 4> pos = {{
YGEdgeTop,
YGEdgeBottom,
YGEdgeLeft,
YGEdgeRight,
}};
static const std::array<YGDimension, 4> dim = {
{YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}};
static inline float YGNodePaddingAndBorderForAxis(
const YGNodeConstRef node,
const YGFlexDirection axis,
const float widthSize) {
return (node->getLeadingPaddingAndBorder(axis, widthSize) +
node->getTrailingPaddingAndBorder(axis, widthSize))
.unwrap();
}
static inline YGAlign YGNodeAlignItem(const YGNode* node, const YGNode* child) {
const YGAlign align = child->getStyle().alignSelf() == YGAlignAuto
? node->getStyle().alignItems()
: child->getStyle().alignSelf();
if (align == YGAlignBaseline &&
YGFlexDirectionIsColumn(node->getStyle().flexDirection())) {
return YGAlignFlexStart;
}
return align;
}
static float YGBaseline(const YGNodeRef node, void* layoutContext) {
if (node->hasBaselineFunc()) {
Event::publish<Event::NodeBaselineStart>(node);
const float baseline = node->baseline(
node->getLayout().measuredDimensions[YGDimensionWidth],
node->getLayout().measuredDimensions[YGDimensionHeight],
layoutContext);
Event::publish<Event::NodeBaselineEnd>(node);
YGAssertWithNode(
node,
!YGFloatIsUndefined(baseline),
"Expect custom baseline function to not return NaN");
return baseline;
}
YGNodeRef baselineChild = nullptr;
const uint32_t childCount = YGNodeGetChildCount(node);
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef child = YGNodeGetChild(node, i);
if (child->getLineIndex() > 0) {
break;
}
if (child->getStyle().positionType() == YGPositionTypeAbsolute) {
continue;
}
if (YGNodeAlignItem(node, child) == YGAlignBaseline ||
child->isReferenceBaseline()) {
baselineChild = child;
break;
}
if (baselineChild == nullptr) {
baselineChild = child;
}
}
if (baselineChild == nullptr) {
return node->getLayout().measuredDimensions[YGDimensionHeight];
}
const float baseline = YGBaseline(baselineChild, layoutContext);
return baseline + baselineChild->getLayout().position[YGEdgeTop];
}
static bool YGIsBaselineLayout(const YGNodeRef node) {
if (YGFlexDirectionIsColumn(node->getStyle().flexDirection())) {
return false;
}
if (node->getStyle().alignItems() == YGAlignBaseline) {
return true;
}
const uint32_t childCount = YGNodeGetChildCount(node);
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef child = YGNodeGetChild(node, i);
if (child->getStyle().positionType() != YGPositionTypeAbsolute &&
child->getStyle().alignSelf() == YGAlignBaseline) {
return true;
}
}
return false;
}
static inline float YGNodeDimWithMargin(
const YGNodeRef node,
const YGFlexDirection axis,
const float widthSize) {
return node->getLayout().measuredDimensions[dim[axis]] +
(node->getLeadingMargin(axis, widthSize) +
node->getTrailingMargin(axis, widthSize))
.unwrap();
}
static inline bool YGNodeIsStyleDimDefined(
const YGNodeRef node,
const YGFlexDirection axis,
const float ownerSize) {
bool isUndefined =
YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value);
return !(
node->getResolvedDimension(dim[axis]).unit == YGUnitAuto ||
node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined ||
(node->getResolvedDimension(dim[axis]).unit == YGUnitPoint &&
!isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) ||
(node->getResolvedDimension(dim[axis]).unit == YGUnitPercent &&
!isUndefined &&
(node->getResolvedDimension(dim[axis]).value < 0.0f ||
YGFloatIsUndefined(ownerSize))));
}
static inline bool YGNodeIsLayoutDimDefined(
const YGNodeRef node,
const YGFlexDirection axis) {
const float value = node->getLayout().measuredDimensions[dim[axis]];
return !YGFloatIsUndefined(value) && value >= 0.0f;
}
static YGFloatOptional YGNodeBoundAxisWithinMinAndMax(
const YGNodeConstRef node,
const YGFlexDirection axis,
const YGFloatOptional value,
const float axisSize) {
YGFloatOptional min;
YGFloatOptional max;
if (YGFlexDirectionIsColumn(axis)) {
min = YGResolveValue(
node->getStyle().minDimensions()[YGDimensionHeight], axisSize);
max = YGResolveValue(
node->getStyle().maxDimensions()[YGDimensionHeight], axisSize);
} else if (YGFlexDirectionIsRow(axis)) {
min = YGResolveValue(
node->getStyle().minDimensions()[YGDimensionWidth], axisSize);
max = YGResolveValue(
node->getStyle().maxDimensions()[YGDimensionWidth], axisSize);
}
if (max >= YGFloatOptional{0} && value > max) {
return max;
}
if (min >= YGFloatOptional{0} && value < min) {
return min;
}
return value;
}
static inline float YGNodeBoundAxis(
const YGNodeRef node,
const YGFlexDirection axis,
const float value,
const float axisSize,
const float widthSize) {
return YGFloatMax(
YGNodeBoundAxisWithinMinAndMax(
node, axis, YGFloatOptional{value}, axisSize)
.unwrap(),
YGNodePaddingAndBorderForAxis(node, axis, widthSize));
}
static void YGNodeSetChildTrailingPosition(
const YGNodeRef node,
const YGNodeRef child,
const YGFlexDirection axis) {
const float size = child->getLayout().measuredDimensions[dim[axis]];
child->setLayoutPosition(
node->getLayout().measuredDimensions[dim[axis]] - size -
child->getLayout().position[pos[axis]],
trailing[axis]);
}
static void YGConstrainMaxSizeForMode(
const YGNodeConstRef node,
const enum YGFlexDirection axis,
const float ownerAxisSize,
const float ownerWidth,
YGMeasureMode* mode,
float* size) {
const YGFloatOptional maxSize =
YGResolveValue(
node->getStyle().maxDimensions()[dim[axis]], ownerAxisSize) +
YGFloatOptional(node->getMarginForAxis(axis, ownerWidth));
switch (*mode) {
case YGMeasureModeExactly:
case YGMeasureModeAtMost:
*size = (maxSize.isUndefined() || *size < maxSize.unwrap())
? *size
: maxSize.unwrap();
break;
case YGMeasureModeUndefined:
if (!maxSize.isUndefined()) {
*mode = YGMeasureModeAtMost;
*size = maxSize.unwrap();
}
break;
}
}
static void YGNodeComputeFlexBasisForChild(
const YGNodeRef node,
const YGNodeRef child,
const float width,
const YGMeasureMode widthMode,
const float height,
const float ownerWidth,
const float ownerHeight,
const YGMeasureMode heightMode,
const YGDirection direction,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount) {
const YGFlexDirection mainAxis =
YGResolveFlexDirection(node->getStyle().flexDirection(), direction);
const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
const float mainAxisSize = isMainAxisRow ? width : height;
const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
float childWidth;
float childHeight;
YGMeasureMode childWidthMeasureMode;
YGMeasureMode childHeightMeasureMode;
const YGFloatOptional resolvedFlexBasis =
YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize);
const bool isRowStyleDimDefined =
YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth);
const bool isColumnStyleDimDefined =
YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, ownerHeight);
if (!resolvedFlexBasis.isUndefined() && !YGFloatIsUndefined(mainAxisSize)) {
if (child->getLayout().computedFlexBasis.isUndefined() ||
(child->getConfig()->isExperimentalFeatureEnabled(
YGExperimentalFeatureWebFlexBasis) &&
child->getLayout().computedFlexBasisGeneration != generationCount)) {
const YGFloatOptional paddingAndBorder = YGFloatOptional(
YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth));
child->setLayoutComputedFlexBasis(
YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder));
}
} else if (isMainAxisRow && isRowStyleDimDefined) {
const YGFloatOptional paddingAndBorder = YGFloatOptional(
YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth));
child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
YGResolveValue(
child->getResolvedDimensions()[YGDimensionWidth], ownerWidth),
paddingAndBorder));
} else if (!isMainAxisRow && isColumnStyleDimDefined) {
const YGFloatOptional paddingAndBorder =
YGFloatOptional(YGNodePaddingAndBorderForAxis(
child, YGFlexDirectionColumn, ownerWidth));
child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
YGResolveValue(
child->getResolvedDimensions()[YGDimensionHeight], ownerHeight),
paddingAndBorder));
} else {
childWidth = YGUndefined;
childHeight = YGUndefined;
childWidthMeasureMode = YGMeasureModeUndefined;
childHeightMeasureMode = YGMeasureModeUndefined;
auto marginRow =
child->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
auto marginColumn =
child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
if (isRowStyleDimDefined) {
childWidth =
YGResolveValue(
child->getResolvedDimensions()[YGDimensionWidth], ownerWidth)
.unwrap() +
marginRow;
childWidthMeasureMode = YGMeasureModeExactly;
}
if (isColumnStyleDimDefined) {
childHeight =
YGResolveValue(
child->getResolvedDimensions()[YGDimensionHeight], ownerHeight)
.unwrap() +
marginColumn;
childHeightMeasureMode = YGMeasureModeExactly;
}
if ((!isMainAxisRow && node->getStyle().overflow() == YGOverflowScroll) ||
node->getStyle().overflow() != YGOverflowScroll) {
if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) {
childWidth = width;
childWidthMeasureMode = YGMeasureModeAtMost;
}
}
if ((isMainAxisRow && node->getStyle().overflow() == YGOverflowScroll) ||
node->getStyle().overflow() != YGOverflowScroll) {
if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) {
childHeight = height;
childHeightMeasureMode = YGMeasureModeAtMost;
}
}
const auto& childStyle = child->getStyle();
if (!childStyle.aspectRatio().isUndefined()) {
if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) {
childHeight = marginColumn +
(childWidth - marginRow) / childStyle.aspectRatio().unwrap();
childHeightMeasureMode = YGMeasureModeExactly;
} else if (
isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) {
childWidth = marginRow +
(childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
childWidthMeasureMode = YGMeasureModeExactly;
}
}
const bool hasExactWidth =
!YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly;
const bool childWidthStretch =
YGNodeAlignItem(node, child) == YGAlignStretch &&
childWidthMeasureMode != YGMeasureModeExactly;
if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth &&
childWidthStretch) {
childWidth = width;
childWidthMeasureMode = YGMeasureModeExactly;
if (!childStyle.aspectRatio().isUndefined()) {
childHeight =
(childWidth - marginRow) / childStyle.aspectRatio().unwrap();
childHeightMeasureMode = YGMeasureModeExactly;
}
}
const bool hasExactHeight =
!YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly;
const bool childHeightStretch =
YGNodeAlignItem(node, child) == YGAlignStretch &&
childHeightMeasureMode != YGMeasureModeExactly;
if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight &&
childHeightStretch) {
childHeight = height;
childHeightMeasureMode = YGMeasureModeExactly;
if (!childStyle.aspectRatio().isUndefined()) {
childWidth =
(childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
childWidthMeasureMode = YGMeasureModeExactly;
}
}
YGConstrainMaxSizeForMode(
child,
YGFlexDirectionRow,
ownerWidth,
ownerWidth,
&childWidthMeasureMode,
&childWidth);
YGConstrainMaxSizeForMode(
child,
YGFlexDirectionColumn,
ownerHeight,
ownerWidth,
&childHeightMeasureMode,
&childHeight);
YGLayoutNodeInternal(
child,
childWidth,
childHeight,
direction,
childWidthMeasureMode,
childHeightMeasureMode,
ownerWidth,
ownerHeight,
false,
LayoutPassReason::kMeasureChild,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax(
child->getLayout().measuredDimensions[dim[mainAxis]],
YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth))));
}
child->setLayoutComputedFlexBasisGeneration(generationCount);
}
static void YGNodeAbsoluteLayoutChild(
const YGNodeRef node,
const YGNodeRef child,
const float width,
const YGMeasureMode widthMode,
const float height,
const YGDirection direction,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount) {
const YGFlexDirection mainAxis =
YGResolveFlexDirection(node->getStyle().flexDirection(), direction);
const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
float childWidth = YGUndefined;
float childHeight = YGUndefined;
YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined;
YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined;
auto marginRow = child->getMarginForAxis(YGFlexDirectionRow, width).unwrap();
auto marginColumn =
child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap();
if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) {
childWidth =
YGResolveValue(child->getResolvedDimensions()[YGDimensionWidth], width)
.unwrap() +
marginRow;
} else {
if (child->isLeadingPositionDefined(YGFlexDirectionRow) &&
child->isTrailingPosDefined(YGFlexDirectionRow)) {
childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] -
(node->getLeadingBorder(YGFlexDirectionRow) +
node->getTrailingBorder(YGFlexDirectionRow)) -
(child->getLeadingPosition(YGFlexDirectionRow, width) +
child->getTrailingPosition(YGFlexDirectionRow, width))
.unwrap();
childWidth =
YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width);
}
}
if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) {
childHeight = YGResolveValue(
child->getResolvedDimensions()[YGDimensionHeight], height)
.unwrap() +
marginColumn;
} else {
if (child->isLeadingPositionDefined(YGFlexDirectionColumn) &&
child->isTrailingPosDefined(YGFlexDirectionColumn)) {
childHeight = node->getLayout().measuredDimensions[YGDimensionHeight] -
(node->getLeadingBorder(YGFlexDirectionColumn) +
node->getTrailingBorder(YGFlexDirectionColumn)) -
(child->getLeadingPosition(YGFlexDirectionColumn, height) +
child->getTrailingPosition(YGFlexDirectionColumn, height))
.unwrap();
childHeight = YGNodeBoundAxis(
child, YGFlexDirectionColumn, childHeight, height, width);
}
}
const auto& childStyle = child->getStyle();
if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) {
if (!childStyle.aspectRatio().isUndefined()) {
if (YGFloatIsUndefined(childWidth)) {
childWidth = marginRow +
(childHeight - marginColumn) * childStyle.aspectRatio().unwrap();
} else if (YGFloatIsUndefined(childHeight)) {
childHeight = marginColumn +
(childWidth - marginRow) / childStyle.aspectRatio().unwrap();
}
}
}
if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) {
childWidthMeasureMode = YGFloatIsUndefined(childWidth)
? YGMeasureModeUndefined
: YGMeasureModeExactly;
childHeightMeasureMode = YGFloatIsUndefined(childHeight)
? YGMeasureModeUndefined
: YGMeasureModeExactly;
if (!isMainAxisRow && YGFloatIsUndefined(childWidth) &&
widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) &&
width > 0) {
childWidth = width;
childWidthMeasureMode = YGMeasureModeAtMost;
}
YGLayoutNodeInternal(
child,
childWidth,
childHeight,
direction,
childWidthMeasureMode,
childHeightMeasureMode,
childWidth,
childHeight,
false,
LayoutPassReason::kAbsMeasureChild,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] +
child->getMarginForAxis(YGFlexDirectionRow, width).unwrap();
childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] +
child->getMarginForAxis(YGFlexDirectionColumn, width).unwrap();
}
YGLayoutNodeInternal(
child,
childWidth,
childHeight,
direction,
YGMeasureModeExactly,
YGMeasureModeExactly,
childWidth,
childHeight,
true,
LayoutPassReason::kAbsLayout,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
if (child->isTrailingPosDefined(mainAxis) &&
!child->isLeadingPositionDefined(mainAxis)) {
child->setLayoutPosition(
node->getLayout().measuredDimensions[dim[mainAxis]] -
child->getLayout().measuredDimensions[dim[mainAxis]] -
node->getTrailingBorder(mainAxis) -
child->getTrailingMargin(mainAxis, isMainAxisRow ? width : height)
.unwrap() -
child->getTrailingPosition(mainAxis, isMainAxisRow ? width : height)
.unwrap(),
leading[mainAxis]);
} else if (
!child->isLeadingPositionDefined(mainAxis) &&
node->getStyle().justifyContent() == YGJustifyCenter) {
child->setLayoutPosition(
(node->getLayout().measuredDimensions[dim[mainAxis]] -
child->getLayout().measuredDimensions[dim[mainAxis]]) /
2.0f,
leading[mainAxis]);
} else if (
!child->isLeadingPositionDefined(mainAxis) &&
node->getStyle().justifyContent() == YGJustifyFlexEnd) {
child->setLayoutPosition(
(node->getLayout().measuredDimensions[dim[mainAxis]] -
child->getLayout().measuredDimensions[dim[mainAxis]]),
leading[mainAxis]);
} else if (
node->getConfig()->isExperimentalFeatureEnabled(
YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge) &&
child->isLeadingPositionDefined(mainAxis)) {
child->setLayoutPosition(
child->getLeadingPosition(
mainAxis, node->getLayout().measuredDimensions[dim[mainAxis]])
.unwrap() +
node->getLeadingBorder(mainAxis) +
child
->getLeadingMargin(
mainAxis,
node->getLayout().measuredDimensions[dim[mainAxis]])
.unwrap(),
leading[mainAxis]);
}
if (child->isTrailingPosDefined(crossAxis) &&
!child->isLeadingPositionDefined(crossAxis)) {
child->setLayoutPosition(
node->getLayout().measuredDimensions[dim[crossAxis]] -
child->getLayout().measuredDimensions[dim[crossAxis]] -
node->getTrailingBorder(crossAxis) -
child->getTrailingMargin(crossAxis, isMainAxisRow ? height : width)
.unwrap() -
child
->getTrailingPosition(crossAxis, isMainAxisRow ? height : width)
.unwrap(),
leading[crossAxis]);
} else if (
!child->isLeadingPositionDefined(crossAxis) &&
YGNodeAlignItem(node, child) == YGAlignCenter) {
child->setLayoutPosition(
(node->getLayout().measuredDimensions[dim[crossAxis]] -
child->getLayout().measuredDimensions[dim[crossAxis]]) /
2.0f,
leading[crossAxis]);
} else if (
!child->isLeadingPositionDefined(crossAxis) &&
((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^
(node->getStyle().flexWrap() == YGWrapWrapReverse))) {
child->setLayoutPosition(
(node->getLayout().measuredDimensions[dim[crossAxis]] -
child->getLayout().measuredDimensions[dim[crossAxis]]),
leading[crossAxis]);
} else if (
node->getConfig()->isExperimentalFeatureEnabled(
YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge) &&
child->isLeadingPositionDefined(crossAxis)) {
child->setLayoutPosition(
child->getLeadingPosition(
crossAxis,
node->getLayout().measuredDimensions[dim[crossAxis]])
.unwrap() +
node->getLeadingBorder(crossAxis) +
child
->getLeadingMargin(
crossAxis,
node->getLayout().measuredDimensions[dim[crossAxis]])
.unwrap(),
leading[crossAxis]);
}
}
static void YGNodeWithMeasureFuncSetMeasuredDimensions(
const YGNodeRef node,
float availableWidth,
float availableHeight,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight,
LayoutData& layoutMarkerData,
void* const layoutContext,
const LayoutPassReason reason) {
YGAssertWithNode(
node,
node->hasMeasureFunc(),
"Expected node to have custom measure function");
if (widthMeasureMode == YGMeasureModeUndefined) {
availableWidth = YGUndefined;
}
if (heightMeasureMode == YGMeasureModeUndefined) {
availableHeight = YGUndefined;
}
const auto& padding = node->getLayout().padding;
const auto& border = node->getLayout().border;
const float paddingAndBorderAxisRow = padding[YGEdgeLeft] +
padding[YGEdgeRight] + border[YGEdgeLeft] + border[YGEdgeRight];
const float paddingAndBorderAxisColumn = padding[YGEdgeTop] +
padding[YGEdgeBottom] + border[YGEdgeTop] + border[YGEdgeBottom];
const float innerWidth = YGFloatIsUndefined(availableWidth)
? availableWidth
: YGFloatMax(0, availableWidth - paddingAndBorderAxisRow);
const float innerHeight = YGFloatIsUndefined(availableHeight)
? availableHeight
: YGFloatMax(0, availableHeight - paddingAndBorderAxisColumn);
if (widthMeasureMode == YGMeasureModeExactly &&
heightMeasureMode == YGMeasureModeExactly) {
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node, YGFlexDirectionRow, availableWidth, ownerWidth, ownerWidth),
YGDimensionWidth);
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionColumn,
availableHeight,
ownerHeight,
ownerWidth),
YGDimensionHeight);
} else {
Event::publish<Event::MeasureCallbackStart>(node);
const YGSize measuredSize = node->measure(
innerWidth,
widthMeasureMode,
innerHeight,
heightMeasureMode,
layoutContext);
layoutMarkerData.measureCallbacks += 1;
layoutMarkerData.measureCallbackReasonsCount[static_cast<size_t>(reason)] +=
1;
Event::publish<Event::MeasureCallbackEnd>(
node,
{layoutContext,
innerWidth,
widthMeasureMode,
innerHeight,
heightMeasureMode,
measuredSize.width,
measuredSize.height,
reason});
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionRow,
(widthMeasureMode == YGMeasureModeUndefined ||
widthMeasureMode == YGMeasureModeAtMost)
? measuredSize.width + paddingAndBorderAxisRow
: availableWidth,
ownerWidth,
ownerWidth),
YGDimensionWidth);
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionColumn,
(heightMeasureMode == YGMeasureModeUndefined ||
heightMeasureMode == YGMeasureModeAtMost)
? measuredSize.height + paddingAndBorderAxisColumn
: availableHeight,
ownerHeight,
ownerWidth),
YGDimensionHeight);
}
}
static void YGNodeEmptyContainerSetMeasuredDimensions(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight) {
const auto& padding = node->getLayout().padding;
const auto& border = node->getLayout().border;
float width = availableWidth;
if (widthMeasureMode == YGMeasureModeUndefined ||
widthMeasureMode == YGMeasureModeAtMost) {
width = padding[YGEdgeLeft] + padding[YGEdgeRight] + border[YGEdgeLeft] +
border[YGEdgeRight];
}
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(node, YGFlexDirectionRow, width, ownerWidth, ownerWidth),
YGDimensionWidth);
float height = availableHeight;
if (heightMeasureMode == YGMeasureModeUndefined ||
heightMeasureMode == YGMeasureModeAtMost) {
height = padding[YGEdgeTop] + padding[YGEdgeBottom] + border[YGEdgeTop] +
border[YGEdgeBottom];
}
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node, YGFlexDirectionColumn, height, ownerHeight, ownerWidth),
YGDimensionHeight);
}
static bool YGNodeFixedSizeSetMeasuredDimensions(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight) {
if ((!YGFloatIsUndefined(availableWidth) &&
widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) ||
(!YGFloatIsUndefined(availableHeight) &&
heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) ||
(widthMeasureMode == YGMeasureModeExactly &&
heightMeasureMode == YGMeasureModeExactly)) {
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionRow,
YGFloatIsUndefined(availableWidth) ||
(widthMeasureMode == YGMeasureModeAtMost &&
availableWidth < 0.0f)
? 0.0f
: availableWidth,
ownerWidth,
ownerWidth),
YGDimensionWidth);
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionColumn,
YGFloatIsUndefined(availableHeight) ||
(heightMeasureMode == YGMeasureModeAtMost &&
availableHeight < 0.0f)
? 0.0f
: availableHeight,
ownerHeight,
ownerWidth),
YGDimensionHeight);
return true;
}
return false;
}
static void YGZeroOutLayoutRecursively(
const YGNodeRef node,
void* layoutContext) {
node->getLayout() = {};
node->setLayoutDimension(0, 0);
node->setLayoutDimension(0, 1);
node->setHasNewLayout(true);
node->iterChildrenAfterCloningIfNeeded(
YGZeroOutLayoutRecursively, layoutContext);
}
static float YGNodeCalculateAvailableInnerDim(
const YGNodeConstRef node,
const YGDimension dimension,
const float availableDim,
const float paddingAndBorder,
const float ownerDim) {
float availableInnerDim = availableDim - paddingAndBorder;
if (!YGFloatIsUndefined(availableInnerDim)) {
const YGFloatOptional minDimensionOptional =
YGResolveValue(node->getStyle().minDimensions()[dimension], ownerDim);
const float minInnerDim = minDimensionOptional.isUndefined()
? 0.0f
: minDimensionOptional.unwrap() - paddingAndBorder;
const YGFloatOptional maxDimensionOptional =
YGResolveValue(node->getStyle().maxDimensions()[dimension], ownerDim);
const float maxInnerDim = maxDimensionOptional.isUndefined()
? FLT_MAX
: maxDimensionOptional.unwrap() - paddingAndBorder;
availableInnerDim =
YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim);
}
return availableInnerDim;
}
static float YGNodeComputeFlexBasisForChildren(
const YGNodeRef node,
const float availableInnerWidth,
const float availableInnerHeight,
YGMeasureMode widthMeasureMode,
YGMeasureMode heightMeasureMode,
YGDirection direction,
YGFlexDirection mainAxis,
const YGConfigRef config,
bool performLayout,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount) {
float totalOuterFlexBasis = 0.0f;
YGNodeRef singleFlexChild = nullptr;
const YGVector& children = node->getChildren();
YGMeasureMode measureModeMainDim =
YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode;
if (measureModeMainDim == YGMeasureModeExactly) {
for (auto child : children) {
if (child->isNodeFlexible()) {
if (singleFlexChild != nullptr ||
YGFloatsEqual(child->resolveFlexGrow(), 0.0f) ||
YGFloatsEqual(child->resolveFlexShrink(), 0.0f)) {
singleFlexChild = nullptr;
break;
} else {
singleFlexChild = child;
}
}
}
}
for (auto child : children) {
child->resolveDimension();
if (child->getStyle().display() == YGDisplayNone) {
YGZeroOutLayoutRecursively(child, layoutContext);
child->setHasNewLayout(true);
child->setDirty(false);
continue;
}
if (performLayout) {
const YGDirection childDirection = child->resolveDirection(direction);
const float mainDim = YGFlexDirectionIsRow(mainAxis)
? availableInnerWidth
: availableInnerHeight;
const float crossDim = YGFlexDirectionIsRow(mainAxis)
? availableInnerHeight
: availableInnerWidth;
child->setPosition(
childDirection, mainDim, crossDim, availableInnerWidth);
}
if (child->getStyle().positionType() == YGPositionTypeAbsolute) {
continue;
}
if (child == singleFlexChild) {
child->setLayoutComputedFlexBasisGeneration(generationCount);
child->setLayoutComputedFlexBasis(YGFloatOptional(0));
} else {
YGNodeComputeFlexBasisForChild(
node,
child,
availableInnerWidth,
widthMeasureMode,
availableInnerHeight,
availableInnerWidth,
availableInnerHeight,
heightMeasureMode,
direction,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
}
totalOuterFlexBasis +=
(child->getLayout().computedFlexBasis +
child->getMarginForAxis(mainAxis, availableInnerWidth))
.unwrap();
}
return totalOuterFlexBasis;
}
static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
const YGNodeRef& node,
const YGDirection ownerDirection,
const float mainAxisownerSize,
const float availableInnerWidth,
const float availableInnerMainDim,
const uint32_t startOfLineIndex,
const uint32_t lineCount) {
YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {};
flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
const YGFlexDirection mainAxis = YGResolveFlexDirection(
node->getStyle().flexDirection(), node->resolveDirection(ownerDirection));
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
const float gap = node->getGapForAxis(mainAxis, availableInnerWidth).unwrap();
uint32_t endOfLineIndex = startOfLineIndex;
for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) {
const YGNodeRef child = node->getChild(endOfLineIndex);
if (child->getStyle().display() == YGDisplayNone ||
child->getStyle().positionType() == YGPositionTypeAbsolute) {
continue;
}
const bool isFirstElementInLine = (endOfLineIndex - startOfLineIndex) == 0;
child->setLineIndex(lineCount);
const float childMarginMainAxis =
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap();
const float childLeadingGapMainAxis = isFirstElementInLine ? 0.0f : gap;
const float flexBasisWithMinAndMaxConstraints =
YGNodeBoundAxisWithinMinAndMax(
child,
mainAxis,
child->getLayout().computedFlexBasis,
mainAxisownerSize)
.unwrap();
if (sizeConsumedOnCurrentLineIncludingMinConstraint +
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis >
availableInnerMainDim &&
isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
break;
}
sizeConsumedOnCurrentLineIncludingMinConstraint +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis;
flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
flexBasisWithMinAndMaxConstraints + childMarginMainAxis +
childLeadingGapMainAxis;
flexAlgoRowMeasurement.itemsOnLine++;
if (child->isNodeFlexible()) {
flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow();
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors +=
-child->resolveFlexShrink() *
child->getLayout().computedFlexBasis.unwrap();
}
flexAlgoRowMeasurement.relativeChildren.push_back(child);
}
if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 &&
flexAlgoRowMeasurement.totalFlexGrowFactors < 1) {
flexAlgoRowMeasurement.totalFlexGrowFactors = 1;
}
if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 &&
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) {
flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1;
}
flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex;
return flexAlgoRowMeasurement;
}
static float YGDistributeFreeSpaceSecondPass(
YGCollectFlexItemsRowValues& collectedFlexItemsValues,
const YGNodeRef node,
const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis,
const float mainAxisownerSize,
const float availableInnerMainDim,
const float availableInnerCrossDim,
const float availableInnerWidth,
const float availableInnerHeight,
const bool mainAxisOverflows,
const YGMeasureMode measureModeCrossDim,
const bool performLayout,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount) {
float childFlexBasis = 0;
float flexShrinkScaledFactor = 0;
float flexGrowFactor = 0;
float deltaFreeSpace = 0;
const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
childFlexBasis = YGNodeBoundAxisWithinMinAndMax(
currentRelativeChild,
mainAxis,
currentRelativeChild->getLayout().computedFlexBasis,
mainAxisownerSize)
.unwrap();
float updatedMainSize = childFlexBasis;
if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace < 0) {
flexShrinkScaledFactor =
-currentRelativeChild->resolveFlexShrink() * childFlexBasis;
if (flexShrinkScaledFactor != 0) {
float childSize;
if (!YGFloatIsUndefined(
collectedFlexItemsValues.totalFlexShrinkScaledFactors) &&
collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
childSize = childFlexBasis + flexShrinkScaledFactor;
} else {
childSize = childFlexBasis +
(collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors) *
flexShrinkScaledFactor;
}
updatedMainSize = YGNodeBoundAxis(
currentRelativeChild,
mainAxis,
childSize,
availableInnerMainDim,
availableInnerWidth);
}
} else if (
!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace > 0) {
flexGrowFactor = currentRelativeChild->resolveFlexGrow();
if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
updatedMainSize = YGNodeBoundAxis(
currentRelativeChild,
mainAxis,
childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors *
flexGrowFactor,
availableInnerMainDim,
availableInnerWidth);
}
}
deltaFreeSpace += updatedMainSize - childFlexBasis;
const float marginMain =
currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth)
.unwrap();
const float marginCross =
currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth)
.unwrap();
float childCrossSize;
float childMainSize = updatedMainSize + marginMain;
YGMeasureMode childCrossMeasureMode;
YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
const auto& childStyle = currentRelativeChild->getStyle();
if (!childStyle.aspectRatio().isUndefined()) {
childCrossSize = isMainAxisRow
? (childMainSize - marginMain) / childStyle.aspectRatio().unwrap()
: (childMainSize - marginMain) * childStyle.aspectRatio().unwrap();
childCrossMeasureMode = YGMeasureModeExactly;
childCrossSize += marginCross;
} else if (
!YGFloatIsUndefined(availableInnerCrossDim) &&
!YGNodeIsStyleDimDefined(
currentRelativeChild, crossAxis, availableInnerCrossDim) &&
measureModeCrossDim == YGMeasureModeExactly &&
!(isNodeFlexWrap && mainAxisOverflows) &&
YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
currentRelativeChild->marginLeadingValue(crossAxis).unit !=
YGUnitAuto &&
currentRelativeChild->marginTrailingValue(crossAxis).unit !=
YGUnitAuto) {
childCrossSize = availableInnerCrossDim;
childCrossMeasureMode = YGMeasureModeExactly;
} else if (!YGNodeIsStyleDimDefined(
currentRelativeChild, crossAxis, availableInnerCrossDim)) {
childCrossSize = availableInnerCrossDim;
childCrossMeasureMode = YGFloatIsUndefined(childCrossSize)
? YGMeasureModeUndefined
: YGMeasureModeAtMost;
} else {
childCrossSize =
YGResolveValue(
currentRelativeChild->getResolvedDimension(dim[crossAxis]),
availableInnerCrossDim)
.unwrap() +
marginCross;
const bool isLoosePercentageMeasurement =
currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit ==
YGUnitPercent &&
measureModeCrossDim != YGMeasureModeExactly;
childCrossMeasureMode =
YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement
? YGMeasureModeUndefined
: YGMeasureModeExactly;
}
YGConstrainMaxSizeForMode(
currentRelativeChild,
mainAxis,
availableInnerMainDim,
availableInnerWidth,
&childMainMeasureMode,
&childMainSize);
YGConstrainMaxSizeForMode(
currentRelativeChild,
crossAxis,
availableInnerCrossDim,
availableInnerWidth,
&childCrossMeasureMode,
&childCrossSize);
const bool requiresStretchLayout =
!YGNodeIsStyleDimDefined(
currentRelativeChild, crossAxis, availableInnerCrossDim) &&
YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
currentRelativeChild->marginLeadingValue(crossAxis).unit !=
YGUnitAuto &&
currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
const YGMeasureMode childWidthMeasureMode =
isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
const YGMeasureMode childHeightMeasureMode =
!isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
const bool isLayoutPass = performLayout && !requiresStretchLayout;
YGLayoutNodeInternal(
currentRelativeChild,
childWidth,
childHeight,
node->getLayout().direction(),
childWidthMeasureMode,
childHeightMeasureMode,
availableInnerWidth,
availableInnerHeight,
isLayoutPass,
isLayoutPass ? LayoutPassReason::kFlexLayout
: LayoutPassReason::kFlexMeasure,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
node->setLayoutHadOverflow(
node->getLayout().hadOverflow() ||
currentRelativeChild->getLayout().hadOverflow());
}
return deltaFreeSpace;
}
static void YGDistributeFreeSpaceFirstPass(
YGCollectFlexItemsRowValues& collectedFlexItemsValues,
const YGFlexDirection mainAxis,
const float mainAxisownerSize,
const float availableInnerMainDim,
const float availableInnerWidth) {
float flexShrinkScaledFactor = 0;
float flexGrowFactor = 0;
float baseMainSize = 0;
float boundMainSize = 0;
float deltaFreeSpace = 0;
for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
float childFlexBasis =
YGNodeBoundAxisWithinMinAndMax(
currentRelativeChild,
mainAxis,
currentRelativeChild->getLayout().computedFlexBasis,
mainAxisownerSize)
.unwrap();
if (collectedFlexItemsValues.remainingFreeSpace < 0) {
flexShrinkScaledFactor =
-currentRelativeChild->resolveFlexShrink() * childFlexBasis;
if (!YGFloatIsUndefined(flexShrinkScaledFactor) &&
flexShrinkScaledFactor != 0) {
baseMainSize = childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.totalFlexShrinkScaledFactors *
flexShrinkScaledFactor;
boundMainSize = YGNodeBoundAxis(
currentRelativeChild,
mainAxis,
baseMainSize,
availableInnerMainDim,
availableInnerWidth);
if (!YGFloatIsUndefined(baseMainSize) &&
!YGFloatIsUndefined(boundMainSize) &&
baseMainSize != boundMainSize) {
deltaFreeSpace += boundMainSize - childFlexBasis;
collectedFlexItemsValues.totalFlexShrinkScaledFactors -=
(-currentRelativeChild->resolveFlexShrink() *
currentRelativeChild->getLayout().computedFlexBasis.unwrap());
}
}
} else if (
!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
collectedFlexItemsValues.remainingFreeSpace > 0) {
flexGrowFactor = currentRelativeChild->resolveFlexGrow();
if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
baseMainSize = childFlexBasis +
collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor;
boundMainSize = YGNodeBoundAxis(
currentRelativeChild,
mainAxis,
baseMainSize,
availableInnerMainDim,
availableInnerWidth);
if (!YGFloatIsUndefined(baseMainSize) &&
!YGFloatIsUndefined(boundMainSize) &&
baseMainSize != boundMainSize) {
deltaFreeSpace += boundMainSize - childFlexBasis;
collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor;
}
}
}
}
collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace;
}
static void YGResolveFlexibleLength(
const YGNodeRef node,
YGCollectFlexItemsRowValues& collectedFlexItemsValues,
const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis,
const float mainAxisownerSize,
const float availableInnerMainDim,
const float availableInnerCrossDim,
const float availableInnerWidth,
const float availableInnerHeight,
const bool mainAxisOverflows,
const YGMeasureMode measureModeCrossDim,
const bool performLayout,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount) {
const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace;
YGDistributeFreeSpaceFirstPass(
collectedFlexItemsValues,
mainAxis,
mainAxisownerSize,
availableInnerMainDim,
availableInnerWidth);
const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass(
collectedFlexItemsValues,
node,
mainAxis,
crossAxis,
mainAxisownerSize,
availableInnerMainDim,
availableInnerCrossDim,
availableInnerWidth,
availableInnerHeight,
mainAxisOverflows,
measureModeCrossDim,
performLayout,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
collectedFlexItemsValues.remainingFreeSpace =
originalFreeSpace - distributedFreeSpace;
}
static void YGJustifyMainAxis(
const YGNodeRef node,
YGCollectFlexItemsRowValues& collectedFlexItemsValues,
const uint32_t startOfLineIndex,
const YGFlexDirection mainAxis,
const YGFlexDirection crossAxis,
const YGMeasureMode measureModeMainDim,
const YGMeasureMode measureModeCrossDim,
const float mainAxisownerSize,
const float ownerWidth,
const float availableInnerMainDim,
const float availableInnerCrossDim,
const float availableInnerWidth,
const bool performLayout,
void* const layoutContext) {
const auto& style = node->getStyle();
const float leadingPaddingAndBorderMain =
node->getLeadingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
const float trailingPaddingAndBorderMain =
node->getTrailingPaddingAndBorder(mainAxis, ownerWidth).unwrap();
const float gap = node->getGapForAxis(mainAxis, ownerWidth).unwrap();
if (measureModeMainDim == YGMeasureModeAtMost &&
collectedFlexItemsValues.remainingFreeSpace > 0) {
if (!style.minDimensions()[dim[mainAxis]].isUndefined() &&
!YGResolveValue(style.minDimensions()[dim[mainAxis]], mainAxisownerSize)
.isUndefined()) {
const float minAvailableMainDim =
YGResolveValue(
style.minDimensions()[dim[mainAxis]], mainAxisownerSize)
.unwrap() -
leadingPaddingAndBorderMain - trailingPaddingAndBorderMain;
const float occupiedSpaceByChildNodes =
availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace;
collectedFlexItemsValues.remainingFreeSpace =
YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes);
} else {
collectedFlexItemsValues.remainingFreeSpace = 0;
}
}
int numberOfAutoMarginsOnCurrentLine = 0;
for (uint32_t i = startOfLineIndex;
i < collectedFlexItemsValues.endOfLineIndex;
i++) {
const YGNodeRef child = node->getChild(i);
if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
numberOfAutoMarginsOnCurrentLine++;
}
if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
numberOfAutoMarginsOnCurrentLine++;
}
}
}
float leadingMainDim = 0;
float betweenMainDim = gap;
const YGJustify justifyContent = node->getStyle().justifyContent();
if (numberOfAutoMarginsOnCurrentLine == 0) {
switch (justifyContent) {
case YGJustifyCenter:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2;
break;
case YGJustifyFlexEnd:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace;
break;
case YGJustifySpaceBetween:
if (collectedFlexItemsValues.itemsOnLine > 1) {
betweenMainDim +=
YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
(collectedFlexItemsValues.itemsOnLine - 1);
}
break;
case YGJustifySpaceEvenly:
leadingMainDim = collectedFlexItemsValues.remainingFreeSpace /
(collectedFlexItemsValues.itemsOnLine + 1);
betweenMainDim += leadingMainDim;
break;
case YGJustifySpaceAround:
leadingMainDim = 0.5f * collectedFlexItemsValues.remainingFreeSpace /
collectedFlexItemsValues.itemsOnLine;
betweenMainDim += leadingMainDim * 2;
break;
case YGJustifyFlexStart:
break;
}
}
collectedFlexItemsValues.mainDim =
leadingPaddingAndBorderMain + leadingMainDim;
collectedFlexItemsValues.crossDim = 0;
float maxAscentForCurrentLine = 0;
float maxDescentForCurrentLine = 0;
bool isNodeBaselineLayout = YGIsBaselineLayout(node);
for (uint32_t i = startOfLineIndex;
i < collectedFlexItemsValues.endOfLineIndex;
i++) {
const YGNodeRef child = node->getChild(i);
const YGStyle& childStyle = child->getStyle();
const YGLayout childLayout = child->getLayout();
const bool isLastChild = i == collectedFlexItemsValues.endOfLineIndex - 1;
if (isLastChild) {
betweenMainDim -= gap;
}
if (childStyle.display() == YGDisplayNone) {
continue;
}
if (childStyle.positionType() == YGPositionTypeAbsolute &&
child->isLeadingPositionDefined(mainAxis)) {
if (performLayout) {
child->setLayoutPosition(
child->getLeadingPosition(mainAxis, availableInnerMainDim)
.unwrap() +
node->getLeadingBorder(mainAxis) +
child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(),
pos[mainAxis]);
}
} else {
if (childStyle.positionType() != YGPositionTypeAbsolute) {
if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim +=
collectedFlexItemsValues.remainingFreeSpace /
numberOfAutoMarginsOnCurrentLine;
}
if (performLayout) {
child->setLayoutPosition(
childLayout.position[pos[mainAxis]] +
collectedFlexItemsValues.mainDim,
pos[mainAxis]);
}
if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
collectedFlexItemsValues.mainDim +=
collectedFlexItemsValues.remainingFreeSpace /
numberOfAutoMarginsOnCurrentLine;
}
bool canSkipFlex =
!performLayout && measureModeCrossDim == YGMeasureModeExactly;
if (canSkipFlex) {
collectedFlexItemsValues.mainDim += betweenMainDim +
child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() +
childLayout.computedFlexBasis.unwrap();
collectedFlexItemsValues.crossDim = availableInnerCrossDim;
} else {
collectedFlexItemsValues.mainDim += betweenMainDim +
YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);
if (isNodeBaselineLayout) {
const float ascent = YGBaseline(child, layoutContext) +
child
->getLeadingMargin(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap();
const float descent =
child->getLayout().measuredDimensions[YGDimensionHeight] +
child
->getMarginForAxis(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap() -
ascent;
maxAscentForCurrentLine =
YGFloatMax(maxAscentForCurrentLine, ascent);
maxDescentForCurrentLine =
YGFloatMax(maxDescentForCurrentLine, descent);
} else {
collectedFlexItemsValues.crossDim = YGFloatMax(
collectedFlexItemsValues.crossDim,
YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));
}
}
} else if (performLayout) {
child->setLayoutPosition(
childLayout.position[pos[mainAxis]] +
node->getLeadingBorder(mainAxis) + leadingMainDim,
pos[mainAxis]);
}
}
}
collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain;
if (isNodeBaselineLayout) {
collectedFlexItemsValues.crossDim =
maxAscentForCurrentLine + maxDescentForCurrentLine;
}
}
static void YGNodelayoutImpl(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGDirection ownerDirection,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight,
const bool performLayout,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
const uint32_t depth,
const uint32_t generationCount,
const LayoutPassReason reason) {
YGAssertWithNode(
node,
YGFloatIsUndefined(availableWidth)
? widthMeasureMode == YGMeasureModeUndefined
: true,
"availableWidth is indefinite so widthMeasureMode must be "
"YGMeasureModeUndefined");
YGAssertWithNode(
node,
YGFloatIsUndefined(availableHeight)
? heightMeasureMode == YGMeasureModeUndefined
: true,
"availableHeight is indefinite so heightMeasureMode must be "
"YGMeasureModeUndefined");
(performLayout ? layoutMarkerData.layouts : layoutMarkerData.measures) += 1;
const YGDirection direction = node->resolveDirection(ownerDirection);
node->setLayoutDirection(direction);
const YGFlexDirection flexRowDirection =
YGResolveFlexDirection(YGFlexDirectionRow, direction);
const YGFlexDirection flexColumnDirection =
YGResolveFlexDirection(YGFlexDirectionColumn, direction);
const YGEdge startEdge =
direction == YGDirectionLTR ? YGEdgeLeft : YGEdgeRight;
const YGEdge endEdge = direction == YGDirectionLTR ? YGEdgeRight : YGEdgeLeft;
const float marginRowLeading =
node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap();
node->setLayoutMargin(marginRowLeading, startEdge);
const float marginRowTrailing =
node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap();
node->setLayoutMargin(marginRowTrailing, endEdge);
const float marginColumnLeading =
node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap();
node->setLayoutMargin(marginColumnLeading, YGEdgeTop);
const float marginColumnTrailing =
node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap();
node->setLayoutMargin(marginColumnTrailing, YGEdgeBottom);
const float marginAxisRow = marginRowLeading + marginRowTrailing;
const float marginAxisColumn = marginColumnLeading + marginColumnTrailing;
node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), startEdge);
node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), endEdge);
node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop);
node->setLayoutBorder(
node->getTrailingBorder(flexColumnDirection), YGEdgeBottom);
node->setLayoutPadding(
node->getLeadingPadding(flexRowDirection, ownerWidth).unwrap(),
startEdge);
node->setLayoutPadding(
node->getTrailingPadding(flexRowDirection, ownerWidth).unwrap(), endEdge);
node->setLayoutPadding(
node->getLeadingPadding(flexColumnDirection, ownerWidth).unwrap(),
YGEdgeTop);
node->setLayoutPadding(
node->getTrailingPadding(flexColumnDirection, ownerWidth).unwrap(),
YGEdgeBottom);
if (node->hasMeasureFunc()) {
YGNodeWithMeasureFuncSetMeasuredDimensions(
node,
availableWidth - marginAxisRow,
availableHeight - marginAxisColumn,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight,
layoutMarkerData,
layoutContext,
reason);
return;
}
const uint32_t childCount = YGNodeGetChildCount(node);
if (childCount == 0) {
YGNodeEmptyContainerSetMeasuredDimensions(
node,
availableWidth - marginAxisRow,
availableHeight - marginAxisColumn,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight);
return;
}
if (!performLayout &&
YGNodeFixedSizeSetMeasuredDimensions(
node,
availableWidth - marginAxisRow,
availableHeight - marginAxisColumn,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight)) {
return;
}
node->cloneChildrenIfNeeded(layoutContext);
node->setLayoutHadOverflow(false);
const YGFlexDirection mainAxis =
YGResolveFlexDirection(node->getStyle().flexDirection(), direction);
const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap;
const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth;
const float paddingAndBorderAxisMain =
YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth);
const float leadingPaddingAndBorderCross =
node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap();
const float trailingPaddingAndBorderCross =
node->getTrailingPaddingAndBorder(crossAxis, ownerWidth).unwrap();
const float paddingAndBorderAxisCross =
leadingPaddingAndBorderCross + trailingPaddingAndBorderCross;
YGMeasureMode measureModeMainDim =
isMainAxisRow ? widthMeasureMode : heightMeasureMode;
YGMeasureMode measureModeCrossDim =
isMainAxisRow ? heightMeasureMode : widthMeasureMode;
const float paddingAndBorderAxisRow =
isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;
const float paddingAndBorderAxisColumn =
isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;
float availableInnerWidth = YGNodeCalculateAvailableInnerDim(
node,
YGDimensionWidth,
availableWidth - marginAxisRow,
paddingAndBorderAxisRow,
ownerWidth);
float availableInnerHeight = YGNodeCalculateAvailableInnerDim(
node,
YGDimensionHeight,
availableHeight - marginAxisColumn,
paddingAndBorderAxisColumn,
ownerHeight);
float availableInnerMainDim =
isMainAxisRow ? availableInnerWidth : availableInnerHeight;
const float availableInnerCrossDim =
isMainAxisRow ? availableInnerHeight : availableInnerWidth;
float totalMainDim = 0;
totalMainDim += YGNodeComputeFlexBasisForChildren(
node,
availableInnerWidth,
availableInnerHeight,
widthMeasureMode,
heightMeasureMode,
direction,
mainAxis,
config,
performLayout,
layoutMarkerData,
layoutContext,
depth,
generationCount);
if (childCount > 1) {
totalMainDim +=
node->getGapForAxis(mainAxis, availableInnerCrossDim).unwrap() *
(childCount - 1);
}
const bool mainAxisOverflows =
(measureModeMainDim != YGMeasureModeUndefined) &&
totalMainDim > availableInnerMainDim;
if (isNodeFlexWrap && mainAxisOverflows &&
measureModeMainDim == YGMeasureModeAtMost) {
measureModeMainDim = YGMeasureModeExactly;
}
uint32_t startOfLineIndex = 0;
uint32_t endOfLineIndex = 0;
uint32_t lineCount = 0;
float totalLineCrossDim = 0;
const float crossAxisGap =
node->getGapForAxis(crossAxis, availableInnerCrossDim).unwrap();
float maxLineMainDim = 0;
YGCollectFlexItemsRowValues collectedFlexItemsValues;
for (; endOfLineIndex < childCount;
lineCount++, startOfLineIndex = endOfLineIndex) {
collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues(
node,
ownerDirection,
mainAxisownerSize,
availableInnerWidth,
availableInnerMainDim,
startOfLineIndex,
lineCount);
endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
const bool canSkipFlex =
!performLayout && measureModeCrossDim == YGMeasureModeExactly;
bool sizeBasedOnContent = false;
if (measureModeMainDim != YGMeasureModeExactly) {
const auto& minDimensions = node->getStyle().minDimensions();
const auto& maxDimensions = node->getStyle().maxDimensions();
const float minInnerWidth =
YGResolveValue(minDimensions[YGDimensionWidth], ownerWidth).unwrap() -
paddingAndBorderAxisRow;
const float maxInnerWidth =
YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap() -
paddingAndBorderAxisRow;
const float minInnerHeight =
YGResolveValue(minDimensions[YGDimensionHeight], ownerHeight)
.unwrap() -
paddingAndBorderAxisColumn;
const float maxInnerHeight =
YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight)
.unwrap() -
paddingAndBorderAxisColumn;
const float minInnerMainDim =
isMainAxisRow ? minInnerWidth : minInnerHeight;
const float maxInnerMainDim =
isMainAxisRow ? maxInnerWidth : maxInnerHeight;
if (!YGFloatIsUndefined(minInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine <
minInnerMainDim) {
availableInnerMainDim = minInnerMainDim;
} else if (
!YGFloatIsUndefined(maxInnerMainDim) &&
collectedFlexItemsValues.sizeConsumedOnCurrentLine >
maxInnerMainDim) {
availableInnerMainDim = maxInnerMainDim;
} else {
bool useLegacyStretchBehaviour =
node->hasErrata(YGErrataStretchFlexBasis);
if (!useLegacyStretchBehaviour &&
((!YGFloatIsUndefined(
collectedFlexItemsValues.totalFlexGrowFactors) &&
collectedFlexItemsValues.totalFlexGrowFactors == 0) ||
(!YGFloatIsUndefined(node->resolveFlexGrow()) &&
node->resolveFlexGrow() == 0))) {
availableInnerMainDim =
collectedFlexItemsValues.sizeConsumedOnCurrentLine;
}
sizeBasedOnContent = !useLegacyStretchBehaviour;
}
}
if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) {
collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim -
collectedFlexItemsValues.sizeConsumedOnCurrentLine;
} else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) {
collectedFlexItemsValues.remainingFreeSpace =
-collectedFlexItemsValues.sizeConsumedOnCurrentLine;
}
if (!canSkipFlex) {
YGResolveFlexibleLength(
node,
collectedFlexItemsValues,
mainAxis,
crossAxis,
mainAxisownerSize,
availableInnerMainDim,
availableInnerCrossDim,
availableInnerWidth,
availableInnerHeight,
mainAxisOverflows,
measureModeCrossDim,
performLayout,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
}
node->setLayoutHadOverflow(
node->getLayout().hadOverflow() |
(collectedFlexItemsValues.remainingFreeSpace < 0));
YGJustifyMainAxis(
node,
collectedFlexItemsValues,
startOfLineIndex,
mainAxis,
crossAxis,
measureModeMainDim,
measureModeCrossDim,
mainAxisownerSize,
ownerWidth,
availableInnerMainDim,
availableInnerCrossDim,
availableInnerWidth,
performLayout,
layoutContext);
float containerCrossAxis = availableInnerCrossDim;
if (measureModeCrossDim == YGMeasureModeUndefined ||
measureModeCrossDim == YGMeasureModeAtMost) {
containerCrossAxis =
YGNodeBoundAxis(
node,
crossAxis,
collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
crossAxisownerSize,
ownerWidth) -
paddingAndBorderAxisCross;
}
if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {
collectedFlexItemsValues.crossDim = availableInnerCrossDim;
}
collectedFlexItemsValues.crossDim =
YGNodeBoundAxis(
node,
crossAxis,
collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
crossAxisownerSize,
ownerWidth) -
paddingAndBorderAxisCross;
if (performLayout) {
for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {
const YGNodeRef child = node->getChild(i);
if (child->getStyle().display() == YGDisplayNone) {
continue;
}
if (child->getStyle().positionType() == YGPositionTypeAbsolute) {
const bool isChildLeadingPosDefined =
child->isLeadingPositionDefined(crossAxis);
if (isChildLeadingPosDefined) {
child->setLayoutPosition(
child->getLeadingPosition(crossAxis, availableInnerCrossDim)
.unwrap() +
node->getLeadingBorder(crossAxis) +
child->getLeadingMargin(crossAxis, availableInnerWidth)
.unwrap(),
pos[crossAxis]);
}
if (!isChildLeadingPosDefined ||
YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) {
child->setLayoutPosition(
node->getLeadingBorder(crossAxis) +
child->getLeadingMargin(crossAxis, availableInnerWidth)
.unwrap(),
pos[crossAxis]);
}
} else {
float leadingCrossDim = leadingPaddingAndBorderCross;
const YGAlign alignItem = YGNodeAlignItem(node, child);
if (alignItem == YGAlignStretch &&
child->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
child->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
if (!YGNodeIsStyleDimDefined(
child, crossAxis, availableInnerCrossDim)) {
float childMainSize =
child->getLayout().measuredDimensions[dim[mainAxis]];
const auto& childStyle = child->getStyle();
float childCrossSize = !childStyle.aspectRatio().isUndefined()
? child->getMarginForAxis(crossAxis, availableInnerWidth)
.unwrap() +
(isMainAxisRow
? childMainSize / childStyle.aspectRatio().unwrap()
: childMainSize * childStyle.aspectRatio().unwrap())
: collectedFlexItemsValues.crossDim;
childMainSize +=
child->getMarginForAxis(mainAxis, availableInnerWidth)
.unwrap();
YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly;
YGConstrainMaxSizeForMode(
child,
mainAxis,
availableInnerMainDim,
availableInnerWidth,
&childMainMeasureMode,
&childMainSize);
YGConstrainMaxSizeForMode(
child,
crossAxis,
availableInnerCrossDim,
availableInnerWidth,
&childCrossMeasureMode,
&childCrossSize);
const float childWidth =
isMainAxisRow ? childMainSize : childCrossSize;
const float childHeight =
!isMainAxisRow ? childMainSize : childCrossSize;
auto alignContent = node->getStyle().alignContent();
auto crossAxisDoesNotGrow =
alignContent != YGAlignStretch && isNodeFlexWrap;
const YGMeasureMode childWidthMeasureMode =
YGFloatIsUndefined(childWidth) ||
(!isMainAxisRow && crossAxisDoesNotGrow)
? YGMeasureModeUndefined
: YGMeasureModeExactly;
const YGMeasureMode childHeightMeasureMode =
YGFloatIsUndefined(childHeight) ||
(isMainAxisRow && crossAxisDoesNotGrow)
? YGMeasureModeUndefined
: YGMeasureModeExactly;
YGLayoutNodeInternal(
child,
childWidth,
childHeight,
direction,
childWidthMeasureMode,
childHeightMeasureMode,
availableInnerWidth,
availableInnerHeight,
true,
LayoutPassReason::kStretch,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
}
} else {
const float remainingCrossDim = containerCrossAxis -
YGNodeDimWithMargin(child, crossAxis, availableInnerWidth);
if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto &&
child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2);
} else if (
child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
} else if (
child->marginLeadingValue(crossAxis).unit == YGUnitAuto) {
leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim);
} else if (alignItem == YGAlignFlexStart) {
} else if (alignItem == YGAlignCenter) {
leadingCrossDim += remainingCrossDim / 2;
} else {
leadingCrossDim += remainingCrossDim;
}
}
child->setLayoutPosition(
child->getLayout().position[pos[crossAxis]] + totalLineCrossDim +
leadingCrossDim,
pos[crossAxis]);
}
}
}
const float appliedCrossGap = lineCount != 0 ? crossAxisGap : 0.0f;
totalLineCrossDim += collectedFlexItemsValues.crossDim + appliedCrossGap;
maxLineMainDim =
YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
}
if (performLayout && ((isNodeFlexWrap && lineCount > 1) || YGIsBaselineLayout(node))) {
float crossDimLead = 0;
float currentLead = leadingPaddingAndBorderCross;
if (!YGFloatIsUndefined(availableInnerCrossDim)) {
const float remainingAlignContentDim =
availableInnerCrossDim - totalLineCrossDim;
switch (node->getStyle().alignContent()) {
case YGAlignFlexEnd:
currentLead += remainingAlignContentDim;
break;
case YGAlignCenter:
currentLead += remainingAlignContentDim / 2;
break;
case YGAlignStretch:
if (availableInnerCrossDim > totalLineCrossDim) {
crossDimLead = remainingAlignContentDim / lineCount;
}
break;
case YGAlignSpaceAround:
if (availableInnerCrossDim > totalLineCrossDim) {
currentLead += remainingAlignContentDim / (2 * lineCount);
if (lineCount > 1) {
crossDimLead = remainingAlignContentDim / lineCount;
}
} else {
currentLead += remainingAlignContentDim / 2;
}
break;
case YGAlignSpaceBetween:
if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {
crossDimLead = remainingAlignContentDim / (lineCount - 1);
}
break;
case YGAlignAuto:
case YGAlignFlexStart:
case YGAlignBaseline:
break;
}
}
uint32_t endIndex = 0;
for (uint32_t i = 0; i < lineCount; i++) {
const uint32_t startIndex = endIndex;
uint32_t ii;
float lineHeight = 0;
float maxAscentForCurrentLine = 0;
float maxDescentForCurrentLine = 0;
for (ii = startIndex; ii < childCount; ii++) {
const YGNodeRef child = node->getChild(ii);
if (child->getStyle().display() == YGDisplayNone) {
continue;
}
if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
if (child->getLineIndex() != i) {
break;
}
if (YGNodeIsLayoutDimDefined(child, crossAxis)) {
lineHeight = YGFloatMax(
lineHeight,
child->getLayout().measuredDimensions[dim[crossAxis]] +
child->getMarginForAxis(crossAxis, availableInnerWidth)
.unwrap());
}
if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
const float ascent = YGBaseline(child, layoutContext) +
child
->getLeadingMargin(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap();
const float descent =
child->getLayout().measuredDimensions[YGDimensionHeight] +
child
->getMarginForAxis(
YGFlexDirectionColumn, availableInnerWidth)
.unwrap() -
ascent;
maxAscentForCurrentLine =
YGFloatMax(maxAscentForCurrentLine, ascent);
maxDescentForCurrentLine =
YGFloatMax(maxDescentForCurrentLine, descent);
lineHeight = YGFloatMax(
lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);
}
}
}
endIndex = ii;
lineHeight += crossDimLead;
currentLead += i != 0 ? crossAxisGap : 0;
if (performLayout) {
for (ii = startIndex; ii < endIndex; ii++) {
const YGNodeRef child = node->getChild(ii);
if (child->getStyle().display() == YGDisplayNone) {
continue;
}
if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
switch (YGNodeAlignItem(node, child)) {
case YGAlignFlexStart: {
child->setLayoutPosition(
currentLead +
child->getLeadingMargin(crossAxis, availableInnerWidth)
.unwrap(),
pos[crossAxis]);
break;
}
case YGAlignFlexEnd: {
child->setLayoutPosition(
currentLead + lineHeight -
child->getTrailingMargin(crossAxis, availableInnerWidth)
.unwrap() -
child->getLayout().measuredDimensions[dim[crossAxis]],
pos[crossAxis]);
break;
}
case YGAlignCenter: {
float childHeight =
child->getLayout().measuredDimensions[dim[crossAxis]];
child->setLayoutPosition(
currentLead + (lineHeight - childHeight) / 2,
pos[crossAxis]);
break;
}
case YGAlignStretch: {
child->setLayoutPosition(
currentLead +
child->getLeadingMargin(crossAxis, availableInnerWidth)
.unwrap(),
pos[crossAxis]);
if (!YGNodeIsStyleDimDefined(
child, crossAxis, availableInnerCrossDim)) {
const float childWidth = isMainAxisRow
? (child->getLayout()
.measuredDimensions[YGDimensionWidth] +
child->getMarginForAxis(mainAxis, availableInnerWidth)
.unwrap())
: lineHeight;
const float childHeight = !isMainAxisRow
? (child->getLayout()
.measuredDimensions[YGDimensionHeight] +
child->getMarginForAxis(crossAxis, availableInnerWidth)
.unwrap())
: lineHeight;
if (!(YGFloatsEqual(
childWidth,
child->getLayout()
.measuredDimensions[YGDimensionWidth]) &&
YGFloatsEqual(
childHeight,
child->getLayout()
.measuredDimensions[YGDimensionHeight]))) {
YGLayoutNodeInternal(
child,
childWidth,
childHeight,
direction,
YGMeasureModeExactly,
YGMeasureModeExactly,
availableInnerWidth,
availableInnerHeight,
true,
LayoutPassReason::kMultilineStretch,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
}
}
break;
}
case YGAlignBaseline: {
child->setLayoutPosition(
currentLead + maxAscentForCurrentLine -
YGBaseline(child, layoutContext) +
child
->getLeadingPosition(
YGFlexDirectionColumn, availableInnerCrossDim)
.unwrap(),
YGEdgeTop);
break;
}
case YGAlignAuto:
case YGAlignSpaceBetween:
case YGAlignSpaceAround:
break;
}
}
}
}
currentLead += lineHeight;
}
}
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionRow,
availableWidth - marginAxisRow,
ownerWidth,
ownerWidth),
YGDimensionWidth);
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
YGFlexDirectionColumn,
availableHeight - marginAxisColumn,
ownerHeight,
ownerWidth),
YGDimensionHeight);
if (measureModeMainDim == YGMeasureModeUndefined ||
(node->getStyle().overflow() != YGOverflowScroll &&
measureModeMainDim == YGMeasureModeAtMost)) {
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth),
dim[mainAxis]);
} else if (
measureModeMainDim == YGMeasureModeAtMost &&
node->getStyle().overflow() == YGOverflowScroll) {
node->setLayoutMeasuredDimension(
YGFloatMax(
YGFloatMin(
availableInnerMainDim + paddingAndBorderAxisMain,
YGNodeBoundAxisWithinMinAndMax(
node,
mainAxis,
YGFloatOptional{maxLineMainDim},
mainAxisownerSize)
.unwrap()),
paddingAndBorderAxisMain),
dim[mainAxis]);
}
if (measureModeCrossDim == YGMeasureModeUndefined ||
(node->getStyle().overflow() != YGOverflowScroll &&
measureModeCrossDim == YGMeasureModeAtMost)) {
node->setLayoutMeasuredDimension(
YGNodeBoundAxis(
node,
crossAxis,
totalLineCrossDim + paddingAndBorderAxisCross,
crossAxisownerSize,
ownerWidth),
dim[crossAxis]);
} else if (
measureModeCrossDim == YGMeasureModeAtMost &&
node->getStyle().overflow() == YGOverflowScroll) {
node->setLayoutMeasuredDimension(
YGFloatMax(
YGFloatMin(
availableInnerCrossDim + paddingAndBorderAxisCross,
YGNodeBoundAxisWithinMinAndMax(
node,
crossAxis,
YGFloatOptional{
totalLineCrossDim + paddingAndBorderAxisCross},
crossAxisownerSize)
.unwrap()),
paddingAndBorderAxisCross),
dim[crossAxis]);
}
if (performLayout && node->getStyle().flexWrap() == YGWrapWrapReverse) {
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef child = YGNodeGetChild(node, i);
if (child->getStyle().positionType() != YGPositionTypeAbsolute) {
child->setLayoutPosition(
node->getLayout().measuredDimensions[dim[crossAxis]] -
child->getLayout().position[pos[crossAxis]] -
child->getLayout().measuredDimensions[dim[crossAxis]],
pos[crossAxis]);
}
}
}
if (performLayout) {
for (auto child : node->getChildren()) {
if (child->getStyle().display() == YGDisplayNone ||
child->getStyle().positionType() != YGPositionTypeAbsolute) {
continue;
}
const bool absolutePercentageAgainstPaddingEdge =
node->getConfig()->isExperimentalFeatureEnabled(
YGExperimentalFeatureAbsolutePercentageAgainstPaddingEdge);
YGNodeAbsoluteLayoutChild(
node,
child,
absolutePercentageAgainstPaddingEdge
? node->getLayout().measuredDimensions[YGDimensionWidth]
: availableInnerWidth,
isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
absolutePercentageAgainstPaddingEdge
? node->getLayout().measuredDimensions[YGDimensionHeight]
: availableInnerHeight,
direction,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount);
}
const bool needsMainTrailingPos = mainAxis == YGFlexDirectionRowReverse ||
mainAxis == YGFlexDirectionColumnReverse;
const bool needsCrossTrailingPos = crossAxis == YGFlexDirectionRowReverse ||
crossAxis == YGFlexDirectionColumnReverse;
if (needsMainTrailingPos || needsCrossTrailingPos) {
for (uint32_t i = 0; i < childCount; i++) {
const YGNodeRef child = node->getChild(i);
if (child->getStyle().display() == YGDisplayNone) {
continue;
}
if (needsMainTrailingPos) {
YGNodeSetChildTrailingPosition(node, child, mainAxis);
}
if (needsCrossTrailingPos) {
YGNodeSetChildTrailingPosition(node, child, crossAxis);
}
}
}
}
}
bool gPrintChanges = false;
bool gPrintSkips = false;
static const char* spacer =
" ";
static const char* YGSpacer(const unsigned long level) {
const size_t spacerLen = strlen(spacer);
if (level > spacerLen) {
return &spacer[0];
} else {
return &spacer[spacerLen - level];
}
}
static const char* YGMeasureModeName(
const YGMeasureMode mode,
const bool performLayout) {
constexpr auto N = enums::count<YGMeasureMode>();
const char* kMeasureModeNames[N] = {"UNDEFINED", "EXACTLY", "AT_MOST"};
const char* kLayoutModeNames[N] = {
"LAY_UNDEFINED", "LAY_EXACTLY", "LAY_AT_MOST"};
if (mode >= N) {
return "";
}
return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode];
}
static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
YGMeasureMode sizeMode,
float size,
float lastComputedSize) {
return sizeMode == YGMeasureModeExactly &&
YGFloatsEqual(size, lastComputedSize);
}
static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
YGMeasureMode sizeMode,
float size,
YGMeasureMode lastSizeMode,
float lastComputedSize) {
return sizeMode == YGMeasureModeAtMost &&
lastSizeMode == YGMeasureModeUndefined &&
(size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize));
}
static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
YGMeasureMode sizeMode,
float size,
YGMeasureMode lastSizeMode,
float lastSize,
float lastComputedSize) {
return lastSizeMode == YGMeasureModeAtMost &&
sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) &&
!YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) &&
lastSize > size &&
(lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize));
}
YOGA_EXPORT float YGRoundValueToPixelGrid(
const double value,
const double pointScaleFactor,
const bool forceCeil,
const bool forceFloor) {
double scaledValue = value * pointScaleFactor;
double fractial = fmod(scaledValue, 1.0);
if (fractial < 0) {
++fractial;
}
if (YGDoubleEqual(fractial, 0)) {
scaledValue = scaledValue - fractial;
} else if (YGDoubleEqual(fractial, 1.0)) {
scaledValue = scaledValue - fractial + 1.0;
} else if (forceCeil) {
scaledValue = scaledValue - fractial + 1.0;
} else if (forceFloor) {
scaledValue = scaledValue - fractial;
} else {
scaledValue = scaledValue - fractial +
(!YGDoubleIsUndefined(fractial) &&
(fractial > 0.5 || YGDoubleEqual(fractial, 0.5))
? 1.0
: 0.0);
}
return (YGDoubleIsUndefined(scaledValue) ||
YGDoubleIsUndefined(pointScaleFactor))
? YGUndefined
: (float) (scaledValue / pointScaleFactor);
}
YOGA_EXPORT bool YGNodeCanUseCachedMeasurement(
const YGMeasureMode widthMode,
const float width,
const YGMeasureMode heightMode,
const float height,
const YGMeasureMode lastWidthMode,
const float lastWidth,
const YGMeasureMode lastHeightMode,
const float lastHeight,
const float lastComputedWidth,
const float lastComputedHeight,
const float marginRow,
const float marginColumn,
const YGConfigRef config) {
if ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) ||
(!YGFloatIsUndefined(lastComputedWidth) && lastComputedWidth < 0)) {
return false;
}
bool useRoundedComparison =
config != nullptr && config->getPointScaleFactor() != 0;
const float effectiveWidth = useRoundedComparison
? YGRoundValueToPixelGrid(
width, config->getPointScaleFactor(), false, false)
: width;
const float effectiveHeight = useRoundedComparison
? YGRoundValueToPixelGrid(
height, config->getPointScaleFactor(), false, false)
: height;
const float effectiveLastWidth = useRoundedComparison
? YGRoundValueToPixelGrid(
lastWidth, config->getPointScaleFactor(), false, false)
: lastWidth;
const float effectiveLastHeight = useRoundedComparison
? YGRoundValueToPixelGrid(
lastHeight, config->getPointScaleFactor(), false, false)
: lastHeight;
const bool hasSameWidthSpec = lastWidthMode == widthMode &&
YGFloatsEqual(effectiveLastWidth, effectiveWidth);
const bool hasSameHeightSpec = lastHeightMode == heightMode &&
YGFloatsEqual(effectiveLastHeight, effectiveHeight);
const bool widthIsCompatible =
hasSameWidthSpec ||
YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
widthMode, width - marginRow, lastComputedWidth) ||
YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
widthMode, width - marginRow, lastWidthMode, lastComputedWidth) ||
YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
widthMode,
width - marginRow,
lastWidthMode,
lastWidth,
lastComputedWidth);
const bool heightIsCompatible =
hasSameHeightSpec ||
YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(
heightMode, height - marginColumn, lastComputedHeight) ||
YGMeasureModeOldSizeIsUnspecifiedAndStillFits(
heightMode,
height - marginColumn,
lastHeightMode,
lastComputedHeight) ||
YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
heightMode,
height - marginColumn,
lastHeightMode,
lastHeight,
lastComputedHeight);
return widthIsCompatible && heightIsCompatible;
}
bool YGLayoutNodeInternal(
const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGDirection ownerDirection,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float ownerWidth,
const float ownerHeight,
const bool performLayout,
const LayoutPassReason reason,
const YGConfigRef config,
LayoutData& layoutMarkerData,
void* const layoutContext,
uint32_t depth,
const uint32_t generationCount) {
YGLayout* layout = &node->getLayout();
depth++;
const bool needToVisitNode =
(node->isDirty() && layout->generationCount != generationCount) ||
layout->lastOwnerDirection != ownerDirection;
if (needToVisitNode) {
layout->nextCachedMeasurementsIndex = 0;
layout->cachedLayout.availableWidth = -1;
layout->cachedLayout.availableHeight = -1;
layout->cachedLayout.widthMeasureMode = YGMeasureModeUndefined;
layout->cachedLayout.heightMeasureMode = YGMeasureModeUndefined;
layout->cachedLayout.computedWidth = -1;
layout->cachedLayout.computedHeight = -1;
}
YGCachedMeasurement* cachedResults = nullptr;
if (node->hasMeasureFunc()) {
const float marginAxisRow =
node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap();
const float marginAxisColumn =
node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap();
if (YGNodeCanUseCachedMeasurement(
widthMeasureMode,
availableWidth,
heightMeasureMode,
availableHeight,
layout->cachedLayout.widthMeasureMode,
layout->cachedLayout.availableWidth,
layout->cachedLayout.heightMeasureMode,
layout->cachedLayout.availableHeight,
layout->cachedLayout.computedWidth,
layout->cachedLayout.computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
cachedResults = &layout->cachedLayout;
} else {
for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
if (YGNodeCanUseCachedMeasurement(
widthMeasureMode,
availableWidth,
heightMeasureMode,
availableHeight,
layout->cachedMeasurements[i].widthMeasureMode,
layout->cachedMeasurements[i].availableWidth,
layout->cachedMeasurements[i].heightMeasureMode,
layout->cachedMeasurements[i].availableHeight,
layout->cachedMeasurements[i].computedWidth,
layout->cachedMeasurements[i].computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
}
}
} else if (performLayout) {
if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&
YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&
layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
cachedResults = &layout->cachedLayout;
}
} else {
for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
if (YGFloatsEqual(
layout->cachedMeasurements[i].availableWidth, availableWidth) &&
YGFloatsEqual(
layout->cachedMeasurements[i].availableHeight, availableHeight) &&
layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
layout->cachedMeasurements[i].heightMeasureMode ==
heightMeasureMode) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
}
}
if (!needToVisitNode && cachedResults != nullptr) {
layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;
layout->measuredDimensions[YGDimensionHeight] =
cachedResults->computedHeight;
(performLayout ? layoutMarkerData.cachedLayouts
: layoutMarkerData.cachedMeasures) += 1;
if (gPrintChanges && gPrintSkips) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.{[skipped] ",
YGSpacer(depth),
depth);
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
availableWidth,
availableHeight,
cachedResults->computedWidth,
cachedResults->computedHeight,
LayoutPassReasonToString(reason));
}
} else {
if (gPrintChanges) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.{%s",
YGSpacer(depth),
depth,
needToVisitNode ? "*" : "");
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, aw: %f ah: %f %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
availableWidth,
availableHeight,
LayoutPassReasonToString(reason));
}
YGNodelayoutImpl(
node,
availableWidth,
availableHeight,
ownerDirection,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight,
performLayout,
config,
layoutMarkerData,
layoutContext,
depth,
generationCount,
reason);
if (gPrintChanges) {
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"%s%d.}%s",
YGSpacer(depth),
depth,
needToVisitNode ? "*" : "");
node->print(layoutContext);
Log::log(
node,
YGLogLevelVerbose,
nullptr,
"wm: %s, hm: %s, d: (%f, %f) %s\n",
YGMeasureModeName(widthMeasureMode, performLayout),
YGMeasureModeName(heightMeasureMode, performLayout),
layout->measuredDimensions[YGDimensionWidth],
layout->measuredDimensions[YGDimensionHeight],
LayoutPassReasonToString(reason));
}
layout->lastOwnerDirection = ownerDirection;
if (cachedResults == nullptr) {
if (layout->nextCachedMeasurementsIndex + 1 >
(uint32_t) layoutMarkerData.maxMeasureCache) {
layoutMarkerData.maxMeasureCache =
layout->nextCachedMeasurementsIndex + 1;
}
if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) {
if (gPrintChanges) {
Log::log(node, YGLogLevelVerbose, nullptr, "Out of cache entries!\n");
}
layout->nextCachedMeasurementsIndex = 0;
}
YGCachedMeasurement* newCacheEntry;
if (performLayout) {
newCacheEntry = &layout->cachedLayout;
} else {
newCacheEntry =
&layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
layout->nextCachedMeasurementsIndex++;
}
newCacheEntry->availableWidth = availableWidth;
newCacheEntry->availableHeight = availableHeight;
newCacheEntry->widthMeasureMode = widthMeasureMode;
newCacheEntry->heightMeasureMode = heightMeasureMode;
newCacheEntry->computedWidth =
layout->measuredDimensions[YGDimensionWidth];
newCacheEntry->computedHeight =
layout->measuredDimensions[YGDimensionHeight];
}
}
if (performLayout) {
node->setLayoutDimension(
node->getLayout().measuredDimensions[YGDimensionWidth],
YGDimensionWidth);
node->setLayoutDimension(
node->getLayout().measuredDimensions[YGDimensionHeight],
YGDimensionHeight);
node->setHasNewLayout(true);
node->setDirty(false);
}
layout->generationCount = generationCount;
LayoutType layoutType;
if (performLayout) {
layoutType = !needToVisitNode && cachedResults == &layout->cachedLayout
? LayoutType::kCachedLayout
: LayoutType::kLayout;
} else {
layoutType = cachedResults != nullptr ? LayoutType::kCachedMeasure
: LayoutType::kMeasure;
}
Event::publish<Event::NodeLayout>(node, {layoutType, layoutContext});
return (needToVisitNode || cachedResults == nullptr);
}
YOGA_EXPORT void YGConfigSetPointScaleFactor(
const YGConfigRef config,
const float pixelsInPoint) {
YGAssertWithConfig(
config,
pixelsInPoint >= 0.0f,
"Scale factor should not be less than zero");
if (pixelsInPoint == 0.0f) {
config->setPointScaleFactor(0.0f);
} else {
config->setPointScaleFactor(pixelsInPoint);
}
}
YOGA_EXPORT float YGConfigGetPointScaleFactor(const YGConfigRef config) {
return config->getPointScaleFactor();
}
static void YGRoundToPixelGrid(
const YGNodeRef node,
const double pointScaleFactor,
const double absoluteLeft,
const double absoluteTop) {
if (pointScaleFactor == 0.0f) {
return;
}
const double nodeLeft = node->getLayout().position[YGEdgeLeft];
const double nodeTop = node->getLayout().position[YGEdgeTop];
const double nodeWidth = node->getLayout().dimensions[YGDimensionWidth];
const double nodeHeight = node->getLayout().dimensions[YGDimensionHeight];
const double absoluteNodeLeft = absoluteLeft + nodeLeft;
const double absoluteNodeTop = absoluteTop + nodeTop;
const double absoluteNodeRight = absoluteNodeLeft + nodeWidth;
const double absoluteNodeBottom = absoluteNodeTop + nodeHeight;
const bool textRounding = node->getNodeType() == YGNodeTypeText;
node->setLayoutPosition(
YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
YGEdgeLeft);
node->setLayoutPosition(
YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
YGEdgeTop);
const bool hasFractionalWidth =
!YGDoubleEqual(fmod(nodeWidth * pointScaleFactor, 1.0), 0) &&
!YGDoubleEqual(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0);
const bool hasFractionalHeight =
!YGDoubleEqual(fmod(nodeHeight * pointScaleFactor, 1.0), 0) &&
!YGDoubleEqual(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0);
node->setLayoutDimension(
YGRoundValueToPixelGrid(
absoluteNodeRight,
pointScaleFactor,
(textRounding && hasFractionalWidth),
(textRounding && !hasFractionalWidth)) -
YGRoundValueToPixelGrid(
absoluteNodeLeft, pointScaleFactor, false, textRounding),
YGDimensionWidth);
node->setLayoutDimension(
YGRoundValueToPixelGrid(
absoluteNodeBottom,
pointScaleFactor,
(textRounding && hasFractionalHeight),
(textRounding && !hasFractionalHeight)) -
YGRoundValueToPixelGrid(
absoluteNodeTop, pointScaleFactor, false, textRounding),
YGDimensionHeight);
const uint32_t childCount = YGNodeGetChildCount(node);
for (uint32_t i = 0; i < childCount; i++) {
YGRoundToPixelGrid(
YGNodeGetChild(node, i),
pointScaleFactor,
absoluteNodeLeft,
absoluteNodeTop);
}
}
YOGA_EXPORT void YGNodeCalculateLayoutWithContext(
const YGNodeRef node,
const float ownerWidth,
const float ownerHeight,
const YGDirection ownerDirection,
void* layoutContext) {
Event::publish<Event::LayoutPassStart>(node, {layoutContext});
LayoutData markerData = {};
gCurrentGenerationCount.fetch_add(1, std::memory_order_relaxed);
node->resolveDimension();
float width = YGUndefined;
YGMeasureMode widthMeasureMode = YGMeasureModeUndefined;
const auto& maxDimensions = node->getStyle().maxDimensions();
if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, ownerWidth)) {
width =
(YGResolveValue(
node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) +
node->getMarginForAxis(YGFlexDirectionRow, ownerWidth))
.unwrap();
widthMeasureMode = YGMeasureModeExactly;
} else if (!YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth)
.isUndefined()) {
width =
YGResolveValue(maxDimensions[YGDimensionWidth], ownerWidth).unwrap();
widthMeasureMode = YGMeasureModeAtMost;
} else {
width = ownerWidth;
widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined
: YGMeasureModeExactly;
}
float height = YGUndefined;
YGMeasureMode heightMeasureMode = YGMeasureModeUndefined;
if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) {
height = (YGResolveValue(
node->getResolvedDimension(dim[YGFlexDirectionColumn]),
ownerHeight) +
node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth))
.unwrap();
heightMeasureMode = YGMeasureModeExactly;
} else if (!YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight)
.isUndefined()) {
height =
YGResolveValue(maxDimensions[YGDimensionHeight], ownerHeight).unwrap();
heightMeasureMode = YGMeasureModeAtMost;
} else {
height = ownerHeight;
heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined
: YGMeasureModeExactly;
}
if (YGLayoutNodeInternal(
node,
width,
height,
ownerDirection,
widthMeasureMode,
heightMeasureMode,
ownerWidth,
ownerHeight,
true,
LayoutPassReason::kInitial,
node->getConfig(),
markerData,
layoutContext,
0,
gCurrentGenerationCount.load(std::memory_order_relaxed))) {
node->setPosition(
node->getLayout().direction(), ownerWidth, ownerHeight, ownerWidth);
YGRoundToPixelGrid(
node, node->getConfig()->getPointScaleFactor(), 0.0f, 0.0f);
#ifdef DEBUG
if (node->getConfig()->shouldPrintTree()) {
YGNodePrint(
node,
(YGPrintOptions) (YGPrintOptionsLayout | YGPrintOptionsChildren | YGPrintOptionsStyle));
}
#endif
}
Event::publish<Event::LayoutPassEnd>(node, {layoutContext, &markerData});
}
YOGA_EXPORT void YGNodeCalculateLayout(
const YGNodeRef node,
const float ownerWidth,
const float ownerHeight,
const YGDirection ownerDirection) {
YGNodeCalculateLayoutWithContext(
node, ownerWidth, ownerHeight, ownerDirection, nullptr);
}
YOGA_EXPORT void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) {
if (logger != nullptr) {
config->setLogger(logger);
} else {
#ifdef ANDROID
config->setLogger(&YGAndroidLog);
#else
config->setLogger(&YGDefaultLog);
#endif
}
}
void YGAssert(const bool condition, const char* message) {
if (!condition) {
Log::log(YGNodeRef{nullptr}, YGLogLevelFatal, nullptr, "%s\n", message);
throwLogicalErrorWithMessage(message);
}
}
void YGAssertWithNode(
const YGNodeRef node,
const bool condition,
const char* message) {
if (!condition) {
Log::log(node, YGLogLevelFatal, nullptr, "%s\n", message);
throwLogicalErrorWithMessage(message);
}
}
void YGAssertWithConfig(
const YGConfigRef config,
const bool condition,
const char* message) {
if (!condition) {
Log::log(config, YGLogLevelFatal, nullptr, "%s\n", message);
throwLogicalErrorWithMessage(message);
}
}
YOGA_EXPORT void YGConfigSetExperimentalFeatureEnabled(
const YGConfigRef config,
const YGExperimentalFeature feature,
const bool enabled) {
config->setExperimentalFeatureEnabled(feature, enabled);
}
YOGA_EXPORT bool YGConfigIsExperimentalFeatureEnabled(
const YGConfigRef config,
const YGExperimentalFeature feature) {
return config->isExperimentalFeatureEnabled(feature);
}
YOGA_EXPORT void YGConfigSetUseWebDefaults(
const YGConfigRef config,
const bool enabled) {
config->setUseWebDefaults(enabled);
}
YOGA_EXPORT bool YGConfigGetUseLegacyStretchBehaviour(
const YGConfigRef config) {
return config->hasErrata(YGErrataStretchFlexBasis);
}
YOGA_EXPORT void YGConfigSetUseLegacyStretchBehaviour(
const YGConfigRef config,
const bool useLegacyStretchBehaviour) {
if (useLegacyStretchBehaviour) {
config->addErrata(YGErrataStretchFlexBasis);
} else {
config->removeErrata(YGErrataStretchFlexBasis);
}
}
bool YGConfigGetUseWebDefaults(const YGConfigRef config) {
return config->useWebDefaults();
}
YOGA_EXPORT void YGConfigSetContext(const YGConfigRef config, void* context) {
config->setContext(context);
}
YOGA_EXPORT void* YGConfigGetContext(const YGConfigRef config) {
return config->getContext();
}
YOGA_EXPORT void YGConfigSetErrata(YGConfigRef config, YGErrata errata) {
config->setErrata(errata);
}
YOGA_EXPORT YGErrata YGConfigGetErrata(YGConfigRef config) {
return config->getErrata();
}
YOGA_EXPORT void YGConfigSetCloneNodeFunc(
const YGConfigRef config,
const YGCloneNodeFunc callback) {
config->setCloneNodeCallback(callback);
}