From 1b83298102a8dec606f83636571e77065808cc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=8A=B2=E9=B9=8F?= Date: Fri, 26 Feb 2021 14:24:23 +0800 Subject: [PATCH] add third party: yoga 1.18.0 --- doric-Qt/doric/doric.pro | 34 +- doric-Qt/doric/yoga/Bitfield.h | 145 + doric-Qt/doric/yoga/CompactValue.h | 184 + doric-Qt/doric/yoga/Utils.cpp | 67 + doric-Qt/doric/yoga/Utils.h | 143 + doric-Qt/doric/yoga/YGConfig.cpp | 44 + doric-Qt/doric/yoga/YGConfig.h | 76 + doric-Qt/doric/yoga/YGEnums.cpp | 226 + doric-Qt/doric/yoga/YGEnums.h | 151 + doric-Qt/doric/yoga/YGFloatOptional.h | 70 + doric-Qt/doric/yoga/YGLayout.cpp | 42 + doric-Qt/doric/yoga/YGLayout.h | 69 + doric-Qt/doric/yoga/YGMacros.h | 53 + doric-Qt/doric/yoga/YGNode.cpp | 585 +++ doric-Qt/doric/yoga/YGNode.h | 323 ++ doric-Qt/doric/yoga/YGNodePrint.cpp | 225 + doric-Qt/doric/yoga/YGNodePrint.h | 25 + doric-Qt/doric/yoga/YGStyle.cpp | 56 + doric-Qt/doric/yoga/YGStyle.h | 203 + doric-Qt/doric/yoga/YGValue.cpp | 12 + doric-Qt/doric/yoga/YGValue.h | 84 + doric-Qt/doric/yoga/Yoga-internal.h | 151 + doric-Qt/doric/yoga/Yoga.cpp | 4405 +++++++++++++++++ doric-Qt/doric/yoga/Yoga.h | 369 ++ doric-Qt/doric/yoga/event/event.cpp | 87 + doric-Qt/doric/yoga/event/event.h | 145 + .../doric/yoga/internal/experiments-inl.h | 32 + doric-Qt/doric/yoga/internal/experiments.cpp | 38 + doric-Qt/doric/yoga/internal/experiments.h | 26 + doric-Qt/doric/yoga/log.cpp | 68 + doric-Qt/doric/yoga/log.h | 38 + 31 files changed, 8174 insertions(+), 2 deletions(-) create mode 100644 doric-Qt/doric/yoga/Bitfield.h create mode 100644 doric-Qt/doric/yoga/CompactValue.h create mode 100644 doric-Qt/doric/yoga/Utils.cpp create mode 100644 doric-Qt/doric/yoga/Utils.h create mode 100644 doric-Qt/doric/yoga/YGConfig.cpp create mode 100644 doric-Qt/doric/yoga/YGConfig.h create mode 100644 doric-Qt/doric/yoga/YGEnums.cpp create mode 100644 doric-Qt/doric/yoga/YGEnums.h create mode 100644 doric-Qt/doric/yoga/YGFloatOptional.h create mode 100644 doric-Qt/doric/yoga/YGLayout.cpp create mode 100644 doric-Qt/doric/yoga/YGLayout.h create mode 100644 doric-Qt/doric/yoga/YGMacros.h create mode 100644 doric-Qt/doric/yoga/YGNode.cpp create mode 100644 doric-Qt/doric/yoga/YGNode.h create mode 100644 doric-Qt/doric/yoga/YGNodePrint.cpp create mode 100644 doric-Qt/doric/yoga/YGNodePrint.h create mode 100644 doric-Qt/doric/yoga/YGStyle.cpp create mode 100644 doric-Qt/doric/yoga/YGStyle.h create mode 100644 doric-Qt/doric/yoga/YGValue.cpp create mode 100644 doric-Qt/doric/yoga/YGValue.h create mode 100644 doric-Qt/doric/yoga/Yoga-internal.h create mode 100644 doric-Qt/doric/yoga/Yoga.cpp create mode 100644 doric-Qt/doric/yoga/Yoga.h create mode 100644 doric-Qt/doric/yoga/event/event.cpp create mode 100644 doric-Qt/doric/yoga/event/event.h create mode 100644 doric-Qt/doric/yoga/internal/experiments-inl.h create mode 100644 doric-Qt/doric/yoga/internal/experiments.cpp create mode 100644 doric-Qt/doric/yoga/internal/experiments.h create mode 100644 doric-Qt/doric/yoga/log.cpp create mode 100644 doric-Qt/doric/yoga/log.h diff --git a/doric-Qt/doric/doric.pro b/doric-Qt/doric/doric.pro index 01a0510f..7e0cd4fb 100644 --- a/doric-Qt/doric/doric.pro +++ b/doric-Qt/doric/doric.pro @@ -41,7 +41,19 @@ SOURCES += \ shader/DoricVLayoutNode.cpp \ shader/DoricViewNode.cpp \ utils/DoricConstant.cpp \ - utils/DoricContextHolder.cpp + utils/DoricContextHolder.cpp \ + yoga/Utils.cpp \ + yoga/YGConfig.cpp \ + yoga/YGEnums.cpp \ + yoga/YGLayout.cpp \ + yoga/YGNode.cpp \ + yoga/YGNodePrint.cpp \ + yoga/YGStyle.cpp \ + yoga/YGValue.cpp \ + yoga/Yoga.cpp \ + yoga/event/event.cpp \ + yoga/internal/experiments.cpp \ + yoga/log.cpp RESOURCES += qml.qrc @@ -93,4 +105,22 @@ HEADERS += \ utils/DoricCountDownLatch.h \ utils/DoricObjectFactory.h \ utils/DoricThreadMode.h \ - utils/DoricUtils.h + utils/DoricUtils.h \ + yoga/Bitfield.h \ + yoga/CompactValue.h \ + yoga/Utils.h \ + yoga/YGConfig.h \ + yoga/YGEnums.h \ + yoga/YGFloatOptional.h \ + yoga/YGLayout.h \ + yoga/YGMacros.h \ + yoga/YGNode.h \ + yoga/YGNodePrint.h \ + yoga/YGStyle.h \ + yoga/YGValue.h \ + yoga/Yoga-internal.h \ + yoga/Yoga.h \ + yoga/event/event.h \ + yoga/internal/experiments-inl.h \ + yoga/internal/experiments.h \ + yoga/log.h diff --git a/doric-Qt/doric/yoga/Bitfield.h b/doric-Qt/doric/yoga/Bitfield.h new file mode 100644 index 00000000..02645961 --- /dev/null +++ b/doric-Qt/doric/yoga/Bitfield.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +namespace facebook { +namespace yoga { + +namespace detail { + +constexpr size_t log2ceil(size_t n) { + return n < 1 ? 0 : (1 + log2ceil(n / 2)); +} + +// The number of bits necessary to represent enums defined with YG_ENUM_SEQ_DECL +template +constexpr size_t bitWidth() { + static_assert( + enums::count() > 0, "Enums must have at least one entries"); + return log2ceil(enums::count() - 1); +} + +// Number of bits needed for a boolean +template <> +constexpr size_t bitWidth() { + return 1; +} + +template +struct BitTraits {}; + +template +struct BitTraits { + // Base cases + static constexpr size_t width(size_t) { return 0; } + static constexpr size_t shift(size_t) { return 0; } +}; + +template +struct BitTraits { + using Rest = BitTraits; + + static constexpr size_t width(size_t idx) { + return idx == 0 ? bitWidth() : Rest::width(idx - 1); + } + + static constexpr size_t shift(size_t idx) { + return idx == 0 ? Rest::width(0) + Rest::shift(0) : Rest::shift(idx - 1); + } + + static constexpr U mask(size_t idx) { + return ((U{1} << width(idx)) - 1) << shift(idx); + } +}; + +template +struct IndexedType { + using Type = typename IndexedType::Type; +}; + +template +struct IndexedType<0, T, Ts...> { + using Type = T; +}; + +} // namespace detail + +template +class Bitfield { + static_assert( + std::is_integral::value, + "Bitfield needs an integral storage type"); + static_assert( + std::is_unsigned::value, + "Bitfield needs an unsigned storage type"); + static_assert(sizeof...(Fields) > 0, "Bitfield needs at least one member"); + + using BitTraits = detail::BitTraits; + +#if !defined(_MSC_VER) || _MSC_VER > 1914 + static_assert( + BitTraits::shift(0) + BitTraits::width(0) <= + std::numeric_limits::digits, + "Specified storage type is too narrow to hold all types"); +#endif + + template + using TypeAt = typename detail::IndexedType::Type; + + template + static constexpr Storage initStorage(Value value, Values... values) { + return ((value << BitTraits::shift(Idx)) & BitTraits::mask(Idx)) | + initStorage(values...); + } + + template + static constexpr Storage initStorage() { + return Storage{0}; + } + + Storage storage_ = 0; + +public: + template + class Ref { + Bitfield& bitfield_; + + public: + Ref(Bitfield& bitfield) : bitfield_(bitfield) {} + Ref& operator=(TypeAt value) { + bitfield_.storage_ = (bitfield_.storage_ & ~BitTraits::mask(Idx)) | + ((value << BitTraits::shift(Idx)) & BitTraits::mask(Idx)); + return *this; + } + operator TypeAt() const { + return const_cast(bitfield_).at(); + } + }; + + constexpr Bitfield() = default; + constexpr Bitfield(Fields... values) : storage_{initStorage<0>(values...)} {} + + template + constexpr TypeAt at() const { + return static_cast>( + (storage_ & BitTraits::mask(Idx)) >> BitTraits::shift(Idx)); + } + + template + Ref at() { + return {*this}; + } +}; + +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/CompactValue.h b/doric-Qt/doric/yoga/CompactValue.h new file mode 100644 index 00000000..be933a16 --- /dev/null +++ b/doric-Qt/doric/yoga/CompactValue.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "YGValue.h" +#include "YGMacros.h" +#include +#include +#include + +static_assert( + std::numeric_limits::is_iec559, + "facebook::yoga::detail::CompactValue only works with IEEE754 floats"); + +#ifdef YOGA_COMPACT_VALUE_TEST +#define VISIBLE_FOR_TESTING public: +#else +#define VISIBLE_FOR_TESTING private: +#endif + +namespace facebook { +namespace yoga { +namespace detail { + +// This class stores YGValue in 32 bits. +// - The value does not matter for Undefined and Auto. NaNs are used for their +// representation. +// - To differentiate between Point and Percent, one exponent bit is used. +// Supported the range [0x40, 0xbf] (0xbf is inclusive for point, but +// exclusive for percent). +// - Value ranges: +// points: 1.08420217e-19f to 36893485948395847680 +// 0x00000000 0x3fffffff +// percent: 1.08420217e-19f to 18446742974197923840 +// 0x40000000 0x7f7fffff +// - Zero is supported, negative zero is not +// - values outside of the representable range are clamped +class YOGA_EXPORT CompactValue { + friend constexpr bool operator==(CompactValue, CompactValue) noexcept; + +public: + static constexpr auto LOWER_BOUND = 1.08420217e-19f; + static constexpr auto UPPER_BOUND_POINT = 36893485948395847680.0f; + static constexpr auto UPPER_BOUND_PERCENT = 18446742974197923840.0f; + + template + static CompactValue of(float value) noexcept { + if (value == 0.0f || (value < LOWER_BOUND && value > -LOWER_BOUND)) { + constexpr auto zero = + Unit == YGUnitPercent ? ZERO_BITS_PERCENT : ZERO_BITS_POINT; + return {Payload{zero}}; + } + + constexpr auto upperBound = + Unit == YGUnitPercent ? UPPER_BOUND_PERCENT : UPPER_BOUND_POINT; + if (value > upperBound || value < -upperBound) { + value = copysignf(upperBound, value); + } + + uint32_t unitBit = Unit == YGUnitPercent ? PERCENT_BIT : 0; + auto data = Payload{value}; + data.repr -= BIAS; + data.repr |= unitBit; + return {data}; + } + + template + static CompactValue ofMaybe(float value) noexcept { + return std::isnan(value) || std::isinf(value) ? ofUndefined() + : of(value); + } + + static constexpr CompactValue ofZero() noexcept { + return CompactValue{Payload{ZERO_BITS_POINT}}; + } + + static constexpr CompactValue ofUndefined() noexcept { + return CompactValue{}; + } + + static constexpr CompactValue ofAuto() noexcept { + return CompactValue{Payload{AUTO_BITS}}; + } + + constexpr CompactValue() noexcept + : payload_(std::numeric_limits::quiet_NaN()) {} + + CompactValue(const YGValue& x) noexcept : payload_(uint32_t{0}) { + switch (x.unit) { + case YGUnitUndefined: + *this = ofUndefined(); + break; + case YGUnitAuto: + *this = ofAuto(); + break; + case YGUnitPoint: + *this = of(x.value); + break; + case YGUnitPercent: + *this = of(x.value); + break; + } + } + + operator YGValue() const noexcept { + switch (payload_.repr) { + case AUTO_BITS: + return YGValueAuto; + case ZERO_BITS_POINT: + return YGValue{0.0f, YGUnitPoint}; + case ZERO_BITS_PERCENT: + return YGValue{0.0f, YGUnitPercent}; + } + + if (std::isnan(payload_.value)) { + return YGValueUndefined; + } + + auto data = payload_; + data.repr &= ~PERCENT_BIT; + data.repr += BIAS; + + return YGValue{data.value, + payload_.repr & 0x40000000 ? YGUnitPercent : YGUnitPoint}; + } + + bool isUndefined() const noexcept { + return ( + payload_.repr != AUTO_BITS && payload_.repr != ZERO_BITS_POINT && + payload_.repr != ZERO_BITS_PERCENT && std::isnan(payload_.value)); + } + + bool isAuto() const noexcept { return payload_.repr == AUTO_BITS; } + +private: + union Payload { + float value; + uint32_t repr; + Payload() = delete; + constexpr Payload(uint32_t r) : repr(r) {} + constexpr Payload(float v) : value(v) {} + }; + + static constexpr uint32_t BIAS = 0x20000000; + static constexpr uint32_t PERCENT_BIT = 0x40000000; + + // these are signaling NaNs with specific bit pattern as payload they will be + // silenced whenever going through an FPU operation on ARM + x86 + static constexpr uint32_t AUTO_BITS = 0x7faaaaaa; + static constexpr uint32_t ZERO_BITS_POINT = 0x7f8f0f0f; + static constexpr uint32_t ZERO_BITS_PERCENT = 0x7f80f0f0; + + constexpr CompactValue(Payload data) noexcept : payload_(data) {} + + Payload payload_; + + VISIBLE_FOR_TESTING uint32_t repr() { return payload_.repr; } +}; + +template <> +CompactValue CompactValue::of(float) noexcept = delete; +template <> +CompactValue CompactValue::of(float) noexcept = delete; +template <> +CompactValue CompactValue::ofMaybe(float) noexcept = delete; +template <> +CompactValue CompactValue::ofMaybe(float) noexcept = delete; + +constexpr bool operator==(CompactValue a, CompactValue b) noexcept { + return a.payload_.repr == b.payload_.repr; +} + +constexpr bool operator!=(CompactValue a, CompactValue b) noexcept { + return !(a == b); +} + +} // namespace detail +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/Utils.cpp b/doric-Qt/doric/yoga/Utils.cpp new file mode 100644 index 00000000..761f3515 --- /dev/null +++ b/doric-Qt/doric/yoga/Utils.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Utils.h" + +using namespace facebook; + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction) { + return YGFlexDirectionIsColumn(flexDirection) + ? YGResolveFlexDirection(YGFlexDirectionRow, direction) + : YGFlexDirectionColumn; +} + +float YGFloatMax(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fmaxf(a, b); + } + return yoga::isUndefined(a) ? b : a; +} + +float YGFloatMin(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fminf(a, b); + } + + return yoga::isUndefined(a) ? b : a; +} + +bool YGValueEqual(const YGValue& a, const YGValue& b) { + if (a.unit != b.unit) { + return false; + } + + if (a.unit == YGUnitUndefined || + (yoga::isUndefined(a.value) && yoga::isUndefined(b.value))) { + return true; + } + + return fabs(a.value - b.value) < 0.0001f; +} + +bool YGFloatsEqual(const float a, const float b) { + if (!yoga::isUndefined(a) && !yoga::isUndefined(b)) { + return fabs(a - b) < 0.0001f; + } + return yoga::isUndefined(a) && yoga::isUndefined(b); +} + +float YGFloatSanitize(const float val) { + return yoga::isUndefined(val) ? 0 : val; +} + +YGFloatOptional YGFloatOptionalMax(YGFloatOptional op1, YGFloatOptional op2) { + if (op1 >= op2) { + return op1; + } + if (op2 > op1) { + return op2; + } + return op1.isUndefined() ? op2 : op1; +} diff --git a/doric-Qt/doric/yoga/Utils.h b/doric-Qt/doric/yoga/Utils.h new file mode 100644 index 00000000..bce8dfca --- /dev/null +++ b/doric-Qt/doric/yoga/Utils.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include "YGNode.h" +#include "Yoga-internal.h" +#include "CompactValue.h" + +// This struct is an helper model to hold the data for step 4 of flexbox algo, +// which is collecting the flex items in a line. +// +// - itemsOnLine: Number of items which can fit in a line considering the +// available Inner dimension, the flex items computed flexbasis and their +// margin. It may be different than the difference between start and end +// indicates because we skip over absolute-positioned items. +// +// - sizeConsumedOnCurrentLine: It is accumulation of the dimensions and margin +// of all the children on the current line. This will be used in order to +// either set the dimensions of the node if none already exist or to compute +// the remaining space left for the flexible children. +// +// - totalFlexGrowFactors: total flex grow factors of flex items which are to be +// layed in the current line +// +// - totalFlexShrinkFactors: total flex shrink factors of flex items which are +// to be layed in the current line +// +// - endOfLineIndex: Its the end index of the last flex item which was examined +// and it may or may not be part of the current line(as it may be absolutely +// positioned or including it may have caused to overshoot availableInnerDim) +// +// - relativeChildren: Maintain a vector of the child nodes that can shrink +// and/or grow. + +struct YGCollectFlexItemsRowValues { + uint32_t itemsOnLine; + float sizeConsumedOnCurrentLine; + float totalFlexGrowFactors; + float totalFlexShrinkScaledFactors; + uint32_t endOfLineIndex; + std::vector relativeChildren; + float remainingFreeSpace; + // The size of the mainDim for the row after considering size, padding, margin + // and border of flex items. This is used to calculate maxLineDim after going + // through all the rows to decide on the main axis size of owner. + float mainDim; + // The size of the crossDim for the row after considering size, padding, + // margin and border of flex items. Used for calculating containers crossSize. + float crossDim; +}; + +bool YGValueEqual(const YGValue& a, const YGValue& b); +inline bool YGValueEqual( + facebook::yoga::detail::CompactValue a, + facebook::yoga::detail::CompactValue b) { + return YGValueEqual((YGValue) a, (YGValue) b); +} + +// This custom float equality function returns true if either absolute +// difference between two floats is less than 0.0001f or both are undefined. +bool YGFloatsEqual(const float a, const float b); + +float YGFloatMax(const float a, const float b); + +YGFloatOptional YGFloatOptionalMax( + const YGFloatOptional op1, + const YGFloatOptional op2); + +float YGFloatMin(const float a, const float b); + +// This custom float comparison function compares the array of float with +// YGFloatsEqual, as the default float comparison operator will not work(Look +// at the comments of YGFloatsEqual function). +template +bool YGFloatArrayEqual( + const std::array& val1, + const std::array& val2) { + bool areEqual = true; + for (std::size_t i = 0; i < size && areEqual; ++i) { + areEqual = YGFloatsEqual(val1[i], val2[i]); + } + return areEqual; +} + +// This function returns 0 if YGFloatIsUndefined(val) is true and val otherwise +float YGFloatSanitize(const float val); + +YGFlexDirection YGFlexDirectionCross( + const YGFlexDirection flexDirection, + const YGDirection direction); + +inline bool YGFlexDirectionIsRow(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionRow || + flexDirection == YGFlexDirectionRowReverse; +} + +inline YGFloatOptional YGResolveValue( + const YGValue value, + const float ownerSize) { + switch (value.unit) { + case YGUnitPoint: + return YGFloatOptional{value.value}; + case YGUnitPercent: + return YGFloatOptional{value.value * ownerSize * 0.01f}; + default: + return YGFloatOptional{}; + } +} + +inline YGFloatOptional YGResolveValue( + yoga::detail::CompactValue value, + float ownerSize) { + return YGResolveValue((YGValue) value, ownerSize); +} + +inline bool YGFlexDirectionIsColumn(const YGFlexDirection flexDirection) { + return flexDirection == YGFlexDirectionColumn || + flexDirection == YGFlexDirectionColumnReverse; +} + +inline YGFlexDirection YGResolveFlexDirection( + const YGFlexDirection flexDirection, + const YGDirection direction) { + if (direction == YGDirectionRTL) { + if (flexDirection == YGFlexDirectionRow) { + return YGFlexDirectionRowReverse; + } else if (flexDirection == YGFlexDirectionRowReverse) { + return YGFlexDirectionRow; + } + } + + return flexDirection; +} + +inline YGFloatOptional YGResolveValueMargin( + yoga::detail::CompactValue value, + const float ownerSize) { + return value.isAuto() ? YGFloatOptional{0} : YGResolveValue(value, ownerSize); +} diff --git a/doric-Qt/doric/yoga/YGConfig.cpp b/doric-Qt/doric/yoga/YGConfig.cpp new file mode 100644 index 00000000..fb72e80c --- /dev/null +++ b/doric-Qt/doric/yoga/YGConfig.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGConfig.h" + +YGConfig::YGConfig(YGLogger logger) : cloneNodeCallback_{nullptr} { + logger_.noContext = logger; + loggerUsesContext_ = false; +} + +void YGConfig::log( + YGConfig* config, + YGNode* node, + YGLogLevel logLevel, + void* logContext, + const char* format, + va_list args) { + if (loggerUsesContext_) { + logger_.withContext(config, node, logLevel, logContext, format, args); + } else { + logger_.noContext(config, node, logLevel, format, args); + } +} + +YGNodeRef YGConfig::cloneNode( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext) { + YGNodeRef clone = nullptr; + if (cloneNodeCallback_.noContext != nullptr) { + clone = cloneNodeUsesContext_ + ? cloneNodeCallback_.withContext(node, owner, childIndex, cloneContext) + : cloneNodeCallback_.noContext(node, owner, childIndex); + } + if (clone == nullptr) { + clone = YGNodeClone(node); + } + return clone; +} diff --git a/doric-Qt/doric/yoga/YGConfig.h b/doric-Qt/doric/yoga/YGConfig.h new file mode 100644 index 00000000..e87d6758 --- /dev/null +++ b/doric-Qt/doric/yoga/YGConfig.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include "Yoga-internal.h" +#include "Yoga.h" + +struct YOGA_EXPORT YGConfig { + using LogWithContextFn = int (*)( + YGConfigRef config, + YGNodeRef node, + YGLogLevel level, + void* context, + const char* format, + va_list args); + using CloneWithContextFn = YGNodeRef (*)( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext); + +private: + union { + CloneWithContextFn withContext; + YGCloneNodeFunc noContext; + } cloneNodeCallback_; + union { + LogWithContextFn withContext; + YGLogger noContext; + } logger_; + bool cloneNodeUsesContext_; + bool loggerUsesContext_; + +public: + bool useWebDefaults = false; + bool useLegacyStretchBehaviour = false; + bool shouldDiffLayoutWithoutLegacyStretchBehaviour = false; + bool printTree = false; + float pointScaleFactor = 1.0f; + std::array()> + experimentalFeatures = {}; + void* context = nullptr; + + YGConfig(YGLogger logger); + void log(YGConfig*, YGNode*, YGLogLevel, void*, const char*, va_list); + void setLogger(YGLogger logger) { + logger_.noContext = logger; + loggerUsesContext_ = false; + } + void setLogger(LogWithContextFn logger) { + logger_.withContext = logger; + loggerUsesContext_ = true; + } + void setLogger(std::nullptr_t) { setLogger(YGLogger{nullptr}); } + + YGNodeRef cloneNode( + YGNodeRef node, + YGNodeRef owner, + int childIndex, + void* cloneContext); + void setCloneNodeCallback(YGCloneNodeFunc cloneNode) { + cloneNodeCallback_.noContext = cloneNode; + cloneNodeUsesContext_ = false; + } + void setCloneNodeCallback(CloneWithContextFn cloneNode) { + cloneNodeCallback_.withContext = cloneNode; + cloneNodeUsesContext_ = true; + } + void setCloneNodeCallback(std::nullptr_t) { + setCloneNodeCallback(YGCloneNodeFunc{nullptr}); + } +}; diff --git a/doric-Qt/doric/yoga/YGEnums.cpp b/doric-Qt/doric/yoga/YGEnums.cpp new file mode 100644 index 00000000..3c130129 --- /dev/null +++ b/doric-Qt/doric/yoga/YGEnums.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGEnums.h" + +const char* YGAlignToString(const YGAlign value) { + switch (value) { + case YGAlignAuto: + return "auto"; + case YGAlignFlexStart: + return "flex-start"; + case YGAlignCenter: + return "center"; + case YGAlignFlexEnd: + return "flex-end"; + case YGAlignStretch: + return "stretch"; + case YGAlignBaseline: + return "baseline"; + case YGAlignSpaceBetween: + return "space-between"; + case YGAlignSpaceAround: + return "space-around"; + } + return "unknown"; +} + +const char* YGDimensionToString(const YGDimension value) { + switch (value) { + case YGDimensionWidth: + return "width"; + case YGDimensionHeight: + return "height"; + } + return "unknown"; +} + +const char* YGDirectionToString(const YGDirection value) { + switch (value) { + case YGDirectionInherit: + return "inherit"; + case YGDirectionLTR: + return "ltr"; + case YGDirectionRTL: + return "rtl"; + } + return "unknown"; +} + +const char* YGDisplayToString(const YGDisplay value) { + switch (value) { + case YGDisplayFlex: + return "flex"; + case YGDisplayNone: + return "none"; + } + return "unknown"; +} + +const char* YGEdgeToString(const YGEdge value) { + switch (value) { + case YGEdgeLeft: + return "left"; + case YGEdgeTop: + return "top"; + case YGEdgeRight: + return "right"; + case YGEdgeBottom: + return "bottom"; + case YGEdgeStart: + return "start"; + case YGEdgeEnd: + return "end"; + case YGEdgeHorizontal: + return "horizontal"; + case YGEdgeVertical: + return "vertical"; + case YGEdgeAll: + return "all"; + } + return "unknown"; +} + +const char* YGExperimentalFeatureToString(const YGExperimentalFeature value) { + switch (value) { + case YGExperimentalFeatureWebFlexBasis: + return "web-flex-basis"; + } + return "unknown"; +} + +const char* YGFlexDirectionToString(const YGFlexDirection value) { + switch (value) { + case YGFlexDirectionColumn: + return "column"; + case YGFlexDirectionColumnReverse: + return "column-reverse"; + case YGFlexDirectionRow: + return "row"; + case YGFlexDirectionRowReverse: + return "row-reverse"; + } + return "unknown"; +} + +const char* YGJustifyToString(const YGJustify value) { + switch (value) { + case YGJustifyFlexStart: + return "flex-start"; + case YGJustifyCenter: + return "center"; + case YGJustifyFlexEnd: + return "flex-end"; + case YGJustifySpaceBetween: + return "space-between"; + case YGJustifySpaceAround: + return "space-around"; + case YGJustifySpaceEvenly: + return "space-evenly"; + } + return "unknown"; +} + +const char* YGLogLevelToString(const YGLogLevel value) { + switch (value) { + case YGLogLevelError: + return "error"; + case YGLogLevelWarn: + return "warn"; + case YGLogLevelInfo: + return "info"; + case YGLogLevelDebug: + return "debug"; + case YGLogLevelVerbose: + return "verbose"; + case YGLogLevelFatal: + return "fatal"; + } + return "unknown"; +} + +const char* YGMeasureModeToString(const YGMeasureMode value) { + switch (value) { + case YGMeasureModeUndefined: + return "undefined"; + case YGMeasureModeExactly: + return "exactly"; + case YGMeasureModeAtMost: + return "at-most"; + } + return "unknown"; +} + +const char* YGNodeTypeToString(const YGNodeType value) { + switch (value) { + case YGNodeTypeDefault: + return "default"; + case YGNodeTypeText: + return "text"; + } + return "unknown"; +} + +const char* YGOverflowToString(const YGOverflow value) { + switch (value) { + case YGOverflowVisible: + return "visible"; + case YGOverflowHidden: + return "hidden"; + case YGOverflowScroll: + return "scroll"; + } + return "unknown"; +} + +const char* YGPositionTypeToString(const YGPositionType value) { + switch (value) { + case YGPositionTypeRelative: + return "relative"; + case YGPositionTypeAbsolute: + return "absolute"; + } + return "unknown"; +} + +const char* YGPrintOptionsToString(const YGPrintOptions value) { + switch (value) { + case YGPrintOptionsLayout: + return "layout"; + case YGPrintOptionsStyle: + return "style"; + case YGPrintOptionsChildren: + return "children"; + } + return "unknown"; +} + +const char* YGUnitToString(const YGUnit value) { + switch (value) { + case YGUnitUndefined: + return "undefined"; + case YGUnitPoint: + return "point"; + case YGUnitPercent: + return "percent"; + case YGUnitAuto: + return "auto"; + } + return "unknown"; +} + +const char* YGWrapToString(const YGWrap value) { + switch (value) { + case YGWrapNoWrap: + return "no-wrap"; + case YGWrapWrap: + return "wrap"; + case YGWrapWrapReverse: + return "wrap-reverse"; + } + return "unknown"; +} diff --git a/doric-Qt/doric/yoga/YGEnums.h b/doric-Qt/doric/yoga/YGEnums.h new file mode 100644 index 00000000..4241281d --- /dev/null +++ b/doric-Qt/doric/yoga/YGEnums.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "YGMacros.h" + +#ifdef __cplusplus +namespace facebook { +namespace yoga { +namespace enums { + +template +constexpr int count(); // can't use `= delete` due to a defect in clang < 3.9 + +namespace detail { +template +constexpr int n() { + return sizeof...(xs); +} +} // namespace detail + +} // namespace enums +} // namespace yoga +} // namespace facebook +#endif + +#define YG_ENUM_DECL(NAME, ...) \ + typedef YG_ENUM_BEGIN(NAME){__VA_ARGS__} YG_ENUM_END(NAME); \ + WIN_EXPORT const char* NAME##ToString(NAME); + +#ifdef __cplusplus +#define YG_ENUM_SEQ_DECL(NAME, ...) \ + YG_ENUM_DECL(NAME, __VA_ARGS__) \ + YG_EXTERN_C_END \ + namespace facebook { \ + namespace yoga { \ + namespace enums { \ + template <> \ + constexpr int count() { \ + return detail::n<__VA_ARGS__>(); \ + } \ + } \ + } \ + } \ + YG_EXTERN_C_BEGIN +#else +#define YG_ENUM_SEQ_DECL YG_ENUM_DECL +#endif + +YG_EXTERN_C_BEGIN + +YG_ENUM_SEQ_DECL( + YGAlign, + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, + YGAlignBaseline, + YGAlignSpaceBetween, + YGAlignSpaceAround); + +YG_ENUM_SEQ_DECL(YGDimension, YGDimensionWidth, YGDimensionHeight) + +YG_ENUM_SEQ_DECL( + YGDirection, + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL) + +YG_ENUM_SEQ_DECL(YGDisplay, YGDisplayFlex, YGDisplayNone) + +YG_ENUM_SEQ_DECL( + YGEdge, + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll) + +YG_ENUM_SEQ_DECL(YGExperimentalFeature, YGExperimentalFeatureWebFlexBasis) + +YG_ENUM_SEQ_DECL( + YGFlexDirection, + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse) + +YG_ENUM_SEQ_DECL( + YGJustify, + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, + YGJustifySpaceEvenly) + +YG_ENUM_SEQ_DECL( + YGLogLevel, + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, + YGLogLevelFatal) + +YG_ENUM_SEQ_DECL( + YGMeasureMode, + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost) + +YG_ENUM_SEQ_DECL(YGNodeType, YGNodeTypeDefault, YGNodeTypeText) + +YG_ENUM_SEQ_DECL( + YGOverflow, + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll) + +YG_ENUM_SEQ_DECL(YGPositionType, YGPositionTypeRelative, YGPositionTypeAbsolute) + +YG_ENUM_DECL( + YGPrintOptions, + YGPrintOptionsLayout = 1, + YGPrintOptionsStyle = 2, + YGPrintOptionsChildren = 4) + +YG_ENUM_SEQ_DECL( + YGUnit, + YGUnitUndefined, + YGUnitPoint, + YGUnitPercent, + YGUnitAuto) + +YG_ENUM_SEQ_DECL(YGWrap, YGWrapNoWrap, YGWrapWrap, YGWrapWrapReverse) + +YG_EXTERN_C_END + +#undef YG_ENUM_DECL +#undef YG_ENUM_SEQ_DECL diff --git a/doric-Qt/doric/yoga/YGFloatOptional.h b/doric-Qt/doric/yoga/YGFloatOptional.h new file mode 100644 index 00000000..e4cf0284 --- /dev/null +++ b/doric-Qt/doric/yoga/YGFloatOptional.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include "Yoga-internal.h" + +struct YGFloatOptional { +private: + float value_ = std::numeric_limits::quiet_NaN(); + +public: + explicit constexpr YGFloatOptional(float value) : value_(value) {} + constexpr YGFloatOptional() = default; + + // returns the wrapped value, or a value x with YGIsUndefined(x) == true + constexpr float unwrap() const { return value_; } + + bool isUndefined() const { return std::isnan(value_); } +}; + +// operators take YGFloatOptional by value, as it is a 32bit value + +inline bool operator==(YGFloatOptional lhs, YGFloatOptional rhs) { + return lhs.unwrap() == rhs.unwrap() || + (lhs.isUndefined() && rhs.isUndefined()); +} +inline bool operator!=(YGFloatOptional lhs, YGFloatOptional rhs) { + return !(lhs == rhs); +} + +inline bool operator==(YGFloatOptional lhs, float rhs) { + return lhs == YGFloatOptional{rhs}; +} +inline bool operator!=(YGFloatOptional lhs, float rhs) { + return !(lhs == rhs); +} + +inline bool operator==(float lhs, YGFloatOptional rhs) { + return rhs == lhs; +} +inline bool operator!=(float lhs, YGFloatOptional rhs) { + return !(lhs == rhs); +} + +inline YGFloatOptional operator+(YGFloatOptional lhs, YGFloatOptional rhs) { + return YGFloatOptional{lhs.unwrap() + rhs.unwrap()}; +} + +inline bool operator>(YGFloatOptional lhs, YGFloatOptional rhs) { + return lhs.unwrap() > rhs.unwrap(); +} + +inline bool operator<(YGFloatOptional lhs, YGFloatOptional rhs) { + return lhs.unwrap() < rhs.unwrap(); +} + +inline bool operator>=(YGFloatOptional lhs, YGFloatOptional rhs) { + return lhs > rhs || lhs == rhs; +} + +inline bool operator<=(YGFloatOptional lhs, YGFloatOptional rhs) { + return lhs < rhs || lhs == rhs; +} diff --git a/doric-Qt/doric/yoga/YGLayout.cpp b/doric-Qt/doric/yoga/YGLayout.cpp new file mode 100644 index 00000000..e43213cd --- /dev/null +++ b/doric-Qt/doric/yoga/YGLayout.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGLayout.h" +#include "Utils.h" + +using namespace facebook; + +bool YGLayout::operator==(YGLayout layout) const { + bool isEqual = YGFloatArrayEqual(position, layout.position) && + YGFloatArrayEqual(dimensions, layout.dimensions) && + YGFloatArrayEqual(margin, layout.margin) && + YGFloatArrayEqual(border, layout.border) && + YGFloatArrayEqual(padding, layout.padding) && + direction() == layout.direction() && + hadOverflow() == layout.hadOverflow() && + lastOwnerDirection == layout.lastOwnerDirection && + nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex && + cachedLayout == layout.cachedLayout && + computedFlexBasis == layout.computedFlexBasis; + + for (uint32_t i = 0; i < YG_MAX_CACHED_RESULT_COUNT && isEqual; ++i) { + isEqual = isEqual && cachedMeasurements[i] == layout.cachedMeasurements[i]; + } + + if (!yoga::isUndefined(measuredDimensions[0]) || + !yoga::isUndefined(layout.measuredDimensions[0])) { + isEqual = + isEqual && (measuredDimensions[0] == layout.measuredDimensions[0]); + } + if (!yoga::isUndefined(measuredDimensions[1]) || + !yoga::isUndefined(layout.measuredDimensions[1])) { + isEqual = + isEqual && (measuredDimensions[1] == layout.measuredDimensions[1]); + } + + return isEqual; +} diff --git a/doric-Qt/doric/yoga/YGLayout.h b/doric-Qt/doric/yoga/YGLayout.h new file mode 100644 index 00000000..658a31f2 --- /dev/null +++ b/doric-Qt/doric/yoga/YGLayout.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include "Bitfield.h" +#include "YGFloatOptional.h" +#include "Yoga-internal.h" + +struct YGLayout { + std::array position = {}; + std::array dimensions = {{YGUndefined, YGUndefined}}; + std::array margin = {}; + std::array border = {}; + std::array padding = {}; + +private: + static constexpr size_t directionIdx = 0; + static constexpr size_t didUseLegacyFlagIdx = 1; + static constexpr size_t doesLegacyStretchFlagAffectsLayoutIdx = 2; + static constexpr size_t hadOverflowIdx = 3; + facebook::yoga::Bitfield flags_ = + {YGDirectionInherit, false, false, false}; + +public: + uint32_t computedFlexBasisGeneration = 0; + YGFloatOptional computedFlexBasis = {}; + + // Instead of recomputing the entire layout every single time, we cache some + // information to break early when nothing changed + uint32_t generationCount = 0; + YGDirection lastOwnerDirection = (YGDirection) -1; + + uint32_t nextCachedMeasurementsIndex = 0; + std::array + cachedMeasurements = {}; + std::array measuredDimensions = {{YGUndefined, YGUndefined}}; + + YGCachedMeasurement cachedLayout = YGCachedMeasurement(); + + YGDirection direction() const { return flags_.at(); } + decltype(flags_)::Ref direction() { + return flags_.at(); + } + + bool didUseLegacyFlag() const { return flags_.at(); } + decltype(flags_)::Ref didUseLegacyFlag() { + return flags_.at(); + } + + bool doesLegacyStretchFlagAffectsLayout() const { + return flags_.at(); + } + decltype(flags_)::Ref + doesLegacyStretchFlagAffectsLayout() { + return flags_.at(); + } + + bool hadOverflow() const { return flags_.at(); } + decltype(flags_)::Ref hadOverflow() { + return flags_.at(); + } + + bool operator==(YGLayout layout) const; + bool operator!=(YGLayout layout) const { return !(*this == layout); } +}; diff --git a/doric-Qt/doric/yoga/YGMacros.h b/doric-Qt/doric/yoga/YGMacros.h new file mode 100644 index 00000000..c6917f1b --- /dev/null +++ b/doric-Qt/doric/yoga/YGMacros.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#ifdef __cplusplus +#define YG_EXTERN_C_BEGIN extern "C" { +#define YG_EXTERN_C_END } +#else +#define YG_EXTERN_C_BEGIN +#define YG_EXTERN_C_END +#endif + +#ifdef _WINDLL +#define WIN_EXPORT __declspec(dllexport) +#else +#define WIN_EXPORT +#endif + +#ifndef YOGA_EXPORT +#ifdef _MSC_VER +#define YOGA_EXPORT +#else +#define YOGA_EXPORT __attribute__((visibility("default"))) +#endif +#endif + +#ifdef NS_ENUM +// Cannot use NSInteger as NSInteger has a different size than int (which is the +// default type of a enum). Therefor when linking the Yoga C library into obj-c +// the header is a missmatch for the Yoga ABI. +#define YG_ENUM_BEGIN(name) NS_ENUM(int, name) +#define YG_ENUM_END(name) +#else +#define YG_ENUM_BEGIN(name) enum name +#define YG_ENUM_END(name) name +#endif + +#ifdef __GNUC__ +#define YG_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define YG_DEPRECATED __declspec(deprecated) +#elif __cplusplus >= 201402L +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(deprecated) +#define YG_DEPRECATED [[deprecated]] +#endif +#endif +#endif diff --git a/doric-Qt/doric/yoga/YGNode.cpp b/doric-Qt/doric/yoga/YGNode.cpp new file mode 100644 index 00000000..1d49b007 --- /dev/null +++ b/doric-Qt/doric/yoga/YGNode.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGNode.h" +#include +#include +#include "CompactValue.h" +#include "Utils.h" + +using namespace facebook; +using facebook::yoga::detail::CompactValue; + +YGNode::YGNode(YGNode&& node) { + context_ = node.context_; + flags_ = node.flags_; + measure_ = node.measure_; + baseline_ = node.baseline_; + print_ = node.print_; + dirtied_ = node.dirtied_; + style_ = node.style_; + layout_ = node.layout_; + lineIndex_ = node.lineIndex_; + owner_ = node.owner_; + children_ = std::move(node.children_); + config_ = node.config_; + resolvedDimensions_ = node.resolvedDimensions_; + for (auto c : children_) { + c->setOwner(c); + } +} + +YGNode::YGNode(const YGNode& node, YGConfigRef config) : YGNode{node} { + config_ = config; + if (config->useWebDefaults) { + useWebDefaults(); + } +} + +void YGNode::print(void* printContext) { + if (print_.noContext != nullptr) { + if (flags_.at()) { + print_.withContext(this, printContext); + } else { + print_.noContext(this); + } + } +} + +YGFloatOptional YGNode::getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) const { + if (YGFlexDirectionIsRow(axis)) { + auto leadingPosition = YGComputedEdgeValue( + style_.position(), YGEdgeStart, CompactValue::ofUndefined()); + if (!leadingPosition.isUndefined()) { + return YGResolveValue(leadingPosition, axisSize); + } + } + + auto leadingPosition = YGComputedEdgeValue( + style_.position(), leading[axis], CompactValue::ofUndefined()); + + return leadingPosition.isUndefined() + ? YGFloatOptional{0} + : YGResolveValue(leadingPosition, axisSize); +} + +YGFloatOptional YGNode::getTrailingPosition( + const YGFlexDirection axis, + const float axisSize) const { + if (YGFlexDirectionIsRow(axis)) { + auto trailingPosition = YGComputedEdgeValue( + style_.position(), YGEdgeEnd, CompactValue::ofUndefined()); + if (!trailingPosition.isUndefined()) { + return YGResolveValue(trailingPosition, axisSize); + } + } + + auto trailingPosition = YGComputedEdgeValue( + style_.position(), trailing[axis], CompactValue::ofUndefined()); + + return trailingPosition.isUndefined() + ? YGFloatOptional{0} + : YGResolveValue(trailingPosition, axisSize); +} + +bool YGNode::isLeadingPositionDefined(const YGFlexDirection axis) const { + return (YGFlexDirectionIsRow(axis) && + !YGComputedEdgeValue( + style_.position(), YGEdgeStart, CompactValue::ofUndefined()) + .isUndefined()) || + !YGComputedEdgeValue( + style_.position(), leading[axis], CompactValue::ofUndefined()) + .isUndefined(); +} + +bool YGNode::isTrailingPosDefined(const YGFlexDirection axis) const { + return (YGFlexDirectionIsRow(axis) && + !YGComputedEdgeValue( + style_.position(), YGEdgeEnd, CompactValue::ofUndefined()) + .isUndefined()) || + !YGComputedEdgeValue( + style_.position(), trailing[axis], CompactValue::ofUndefined()) + .isUndefined(); +} + +YGFloatOptional YGNode::getLeadingMargin( + const YGFlexDirection axis, + const float widthSize) const { + if (YGFlexDirectionIsRow(axis) && + !style_.margin()[YGEdgeStart].isUndefined()) { + return YGResolveValueMargin(style_.margin()[YGEdgeStart], widthSize); + } + + return YGResolveValueMargin( + YGComputedEdgeValue( + style_.margin(), leading[axis], CompactValue::ofZero()), + widthSize); +} + +YGFloatOptional YGNode::getTrailingMargin( + const YGFlexDirection axis, + const float widthSize) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { + return YGResolveValueMargin(style_.margin()[YGEdgeEnd], widthSize); + } + + return YGResolveValueMargin( + YGComputedEdgeValue( + style_.margin(), trailing[axis], CompactValue::ofZero()), + widthSize); +} + +YGFloatOptional YGNode::getMarginForAxis( + const YGFlexDirection axis, + const float widthSize) const { + return getLeadingMargin(axis, widthSize) + getTrailingMargin(axis, widthSize); +} + +YGSize YGNode::measure( + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode, + void* layoutContext) { + + return flags_.at() + ? measure_.withContext( + this, width, widthMode, height, heightMode, layoutContext) + : measure_.noContext(this, width, widthMode, height, heightMode); +} + +float YGNode::baseline(float width, float height, void* layoutContext) { + return flags_.at() + ? baseline_.withContext(this, width, height, layoutContext) + : baseline_.noContext(this, width, height); +} + +// Setters + +void YGNode::setMeasureFunc(decltype(YGNode::measure_) measureFunc) { + if (measureFunc.noContext == nullptr) { + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + flags_.at() = YGNodeTypeDefault; + } else { + YGAssertWithNode( + this, + children_.size() == 0, + "Cannot set measure function: Nodes with measure functions cannot have " + "children."); + // TODO: t18095186 Move nodeType to opt-in function and mark appropriate + // places in Litho + setNodeType(YGNodeTypeText); + } + + measure_ = measureFunc; +} + +void YGNode::setMeasureFunc(YGMeasureFunc measureFunc) { + flags_.at() = false; + decltype(YGNode::measure_) m; + m.noContext = measureFunc; + setMeasureFunc(m); +} + +YOGA_EXPORT void YGNode::setMeasureFunc(MeasureWithContextFn measureFunc) { + flags_.at() = true; + decltype(YGNode::measure_) m; + m.withContext = measureFunc; + setMeasureFunc(m); +} + +void YGNode::replaceChild(YGNodeRef child, uint32_t index) { + children_[index] = child; +} + +void YGNode::replaceChild(YGNodeRef oldChild, YGNodeRef newChild) { + std::replace(children_.begin(), children_.end(), oldChild, newChild); +} + +void YGNode::insertChild(YGNodeRef child, uint32_t index) { + children_.insert(children_.begin() + index, child); +} + +void YGNode::setDirty(bool isDirty) { + if (isDirty == flags_.at()) { + return; + } + flags_.at() = isDirty; + if (isDirty && dirtied_) { + dirtied_(this); + } +} + +bool YGNode::removeChild(YGNodeRef child) { + std::vector::iterator p = + std::find(children_.begin(), children_.end(), child); + if (p != children_.end()) { + children_.erase(p); + return true; + } + return false; +} + +void YGNode::removeChild(uint32_t index) { + children_.erase(children_.begin() + index); +} + +void YGNode::setLayoutDirection(YGDirection direction) { + layout_.direction() = direction; +} + +void YGNode::setLayoutMargin(float margin, int index) { + layout_.margin[index] = margin; +} + +void YGNode::setLayoutBorder(float border, int index) { + layout_.border[index] = border; +} + +void YGNode::setLayoutPadding(float padding, int index) { + layout_.padding[index] = padding; +} + +void YGNode::setLayoutLastOwnerDirection(YGDirection direction) { + layout_.lastOwnerDirection = direction; +} + +void YGNode::setLayoutComputedFlexBasis( + const YGFloatOptional computedFlexBasis) { + layout_.computedFlexBasis = computedFlexBasis; +} + +void YGNode::setLayoutPosition(float position, int index) { + layout_.position[index] = position; +} + +void YGNode::setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration) { + layout_.computedFlexBasisGeneration = computedFlexBasisGeneration; +} + +void YGNode::setLayoutMeasuredDimension(float measuredDimension, int index) { + layout_.measuredDimensions[index] = measuredDimension; +} + +void YGNode::setLayoutHadOverflow(bool hadOverflow) { + layout_.hadOverflow() = hadOverflow; +} + +void YGNode::setLayoutDimension(float dimension, int index) { + layout_.dimensions[index] = dimension; +} + +// If both left and right are defined, then use left. Otherwise return +left or +// -right depending on which is defined. +YGFloatOptional YGNode::relativePosition( + const YGFlexDirection axis, + const float axisSize) const { + if (isLeadingPositionDefined(axis)) { + return getLeadingPosition(axis, axisSize); + } + + YGFloatOptional trailingPosition = getTrailingPosition(axis, axisSize); + if (!trailingPosition.isUndefined()) { + trailingPosition = YGFloatOptional{-1 * trailingPosition.unwrap()}; + } + return trailingPosition; +} + +void YGNode::setPosition( + const YGDirection direction, + const float mainSize, + const float crossSize, + const float ownerWidth) { + /* Root nodes should be always layouted as LTR, so we don't return negative + * values. */ + const YGDirection directionRespectingRoot = + owner_ != nullptr ? direction : YGDirectionLTR; + const YGFlexDirection mainAxis = + YGResolveFlexDirection(style_.flexDirection(), directionRespectingRoot); + const YGFlexDirection crossAxis = + YGFlexDirectionCross(mainAxis, directionRespectingRoot); + + const YGFloatOptional relativePositionMain = + relativePosition(mainAxis, mainSize); + const YGFloatOptional relativePositionCross = + relativePosition(crossAxis, crossSize); + + setLayoutPosition( + (getLeadingMargin(mainAxis, ownerWidth) + relativePositionMain).unwrap(), + leading[mainAxis]); + setLayoutPosition( + (getTrailingMargin(mainAxis, ownerWidth) + relativePositionMain).unwrap(), + trailing[mainAxis]); + setLayoutPosition( + (getLeadingMargin(crossAxis, ownerWidth) + relativePositionCross) + .unwrap(), + leading[crossAxis]); + setLayoutPosition( + (getTrailingMargin(crossAxis, ownerWidth) + relativePositionCross) + .unwrap(), + trailing[crossAxis]); +} + +YGValue YGNode::marginLeadingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && + !style_.margin()[YGEdgeStart].isUndefined()) { + return style_.margin()[YGEdgeStart]; + } else { + return style_.margin()[leading[axis]]; + } +} + +YGValue YGNode::marginTrailingValue(const YGFlexDirection axis) const { + if (YGFlexDirectionIsRow(axis) && !style_.margin()[YGEdgeEnd].isUndefined()) { + return style_.margin()[YGEdgeEnd]; + } else { + return style_.margin()[trailing[axis]]; + } +} + +YGValue YGNode::resolveFlexBasisPtr() const { + YGValue flexBasis = style_.flexBasis(); + if (flexBasis.unit != YGUnitAuto && flexBasis.unit != YGUnitUndefined) { + return flexBasis; + } + if (!style_.flex().isUndefined() && style_.flex().unwrap() > 0.0f) { + return flags_.at() ? YGValueAuto : YGValueZero; + } + return YGValueAuto; +} + +void YGNode::resolveDimension() { + using namespace yoga; + const YGStyle& style = getStyle(); + for (auto dim : {YGDimensionWidth, YGDimensionHeight}) { + if (!style.maxDimensions()[dim].isUndefined() && + YGValueEqual(style.maxDimensions()[dim], style.minDimensions()[dim])) { + resolvedDimensions_[dim] = style.maxDimensions()[dim]; + } else { + resolvedDimensions_[dim] = style.dimensions()[dim]; + } + } +} + +YGDirection YGNode::resolveDirection(const YGDirection ownerDirection) { + if (style_.direction() == YGDirectionInherit) { + return ownerDirection > YGDirectionInherit ? ownerDirection + : YGDirectionLTR; + } else { + return style_.direction(); + } +} + +YOGA_EXPORT void YGNode::clearChildren() { + children_.clear(); + children_.shrink_to_fit(); +} + +// Other Methods + +void YGNode::cloneChildrenIfNeeded(void* cloneContext) { + iterChildrenAfterCloningIfNeeded([](YGNodeRef, void*) {}, cloneContext); +} + +void YGNode::markDirtyAndPropogate() { + if (!flags_.at()) { + setDirty(true); + setLayoutComputedFlexBasis(YGFloatOptional()); + if (owner_) { + owner_->markDirtyAndPropogate(); + } + } +} + +void YGNode::markDirtyAndPropogateDownwards() { + flags_.at() = true; + for_each(children_.begin(), children_.end(), [](YGNodeRef childNode) { + childNode->markDirtyAndPropogateDownwards(); + }); +} + +float YGNode::resolveFlexGrow() const { + // Root nodes flexGrow should always be 0 + if (owner_ == nullptr) { + return 0.0; + } + if (!style_.flexGrow().isUndefined()) { + return style_.flexGrow().unwrap(); + } + if (!style_.flex().isUndefined() && style_.flex().unwrap() > 0.0f) { + return style_.flex().unwrap(); + } + return kDefaultFlexGrow; +} + +float YGNode::resolveFlexShrink() const { + if (owner_ == nullptr) { + return 0.0; + } + if (!style_.flexShrink().isUndefined()) { + return style_.flexShrink().unwrap(); + } + if (!flags_.at() && !style_.flex().isUndefined() && + style_.flex().unwrap() < 0.0f) { + return -style_.flex().unwrap(); + } + return flags_.at() ? kWebDefaultFlexShrink + : kDefaultFlexShrink; +} + +bool YGNode::isNodeFlexible() { + return ( + (style_.positionType() == YGPositionTypeRelative) && + (resolveFlexGrow() != 0 || resolveFlexShrink() != 0)); +} + +float YGNode::getLeadingBorder(const YGFlexDirection axis) const { + YGValue leadingBorder; + if (YGFlexDirectionIsRow(axis) && + !style_.border()[YGEdgeStart].isUndefined()) { + leadingBorder = style_.border()[YGEdgeStart]; + if (leadingBorder.value >= 0) { + return leadingBorder.value; + } + } + + leadingBorder = YGComputedEdgeValue( + style_.border(), leading[axis], CompactValue::ofZero()); + return YGFloatMax(leadingBorder.value, 0.0f); +} + +float YGNode::getTrailingBorder(const YGFlexDirection flexDirection) const { + YGValue trailingBorder; + if (YGFlexDirectionIsRow(flexDirection) && + !style_.border()[YGEdgeEnd].isUndefined()) { + trailingBorder = style_.border()[YGEdgeEnd]; + if (trailingBorder.value >= 0.0f) { + return trailingBorder.value; + } + } + + trailingBorder = YGComputedEdgeValue( + style_.border(), trailing[flexDirection], CompactValue::ofZero()); + return YGFloatMax(trailingBorder.value, 0.0f); +} + +YGFloatOptional YGNode::getLeadingPadding( + const YGFlexDirection axis, + const float widthSize) const { + const YGFloatOptional paddingEdgeStart = + YGResolveValue(style_.padding()[YGEdgeStart], widthSize); + if (YGFlexDirectionIsRow(axis) && + !style_.padding()[YGEdgeStart].isUndefined() && + !paddingEdgeStart.isUndefined() && paddingEdgeStart.unwrap() >= 0.0f) { + return paddingEdgeStart; + } + + YGFloatOptional resolvedValue = YGResolveValue( + YGComputedEdgeValue( + style_.padding(), leading[axis], CompactValue::ofZero()), + widthSize); + return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); +} + +YGFloatOptional YGNode::getTrailingPadding( + const YGFlexDirection axis, + const float widthSize) const { + const YGFloatOptional paddingEdgeEnd = + YGResolveValue(style_.padding()[YGEdgeEnd], widthSize); + if (YGFlexDirectionIsRow(axis) && paddingEdgeEnd >= YGFloatOptional{0.0f}) { + return paddingEdgeEnd; + } + + YGFloatOptional resolvedValue = YGResolveValue( + YGComputedEdgeValue( + style_.padding(), trailing[axis], CompactValue::ofZero()), + widthSize); + + return YGFloatOptionalMax(resolvedValue, YGFloatOptional(0.0f)); +} + +YGFloatOptional YGNode::getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const { + return getLeadingPadding(axis, widthSize) + + YGFloatOptional(getLeadingBorder(axis)); +} + +YGFloatOptional YGNode::getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const { + return getTrailingPadding(axis, widthSize) + + YGFloatOptional(getTrailingBorder(axis)); +} + +bool YGNode::didUseLegacyFlag() { + bool didUseLegacyFlag = layout_.didUseLegacyFlag(); + if (didUseLegacyFlag) { + return true; + } + for (const auto& child : children_) { + if (child->layout_.didUseLegacyFlag()) { + didUseLegacyFlag = true; + break; + } + } + return didUseLegacyFlag; +} + +void YGNode::setLayoutDoesLegacyFlagAffectsLayout( + bool doesLegacyFlagAffectsLayout) { + layout_.doesLegacyStretchFlagAffectsLayout() = doesLegacyFlagAffectsLayout; +} + +void YGNode::setLayoutDidUseLegacyFlag(bool didUseLegacyFlag) { + layout_.didUseLegacyFlag() = didUseLegacyFlag; +} + +bool YGNode::isLayoutTreeEqualToNode(const YGNode& node) const { + if (children_.size() != node.children_.size()) { + return false; + } + if (layout_ != node.layout_) { + return false; + } + if (children_.size() == 0) { + return true; + } + + bool isLayoutTreeEqual = true; + YGNodeRef otherNodeChildren = nullptr; + for (std::vector::size_type i = 0; i < children_.size(); ++i) { + otherNodeChildren = node.children_[i]; + isLayoutTreeEqual = + children_[i]->isLayoutTreeEqualToNode(*otherNodeChildren); + if (!isLayoutTreeEqual) { + return false; + } + } + return isLayoutTreeEqual; +} + +void YGNode::reset() { + YGAssertWithNode( + this, + children_.size() == 0, + "Cannot reset a node which still has children attached"); + YGAssertWithNode( + this, owner_ == nullptr, "Cannot reset a node still attached to a owner"); + + clearChildren(); + + auto webDefaults = flags_.at(); + *this = YGNode{getConfig()}; + if (webDefaults) { + useWebDefaults(); + } +} diff --git a/doric-Qt/doric/yoga/YGNode.h b/doric-Qt/doric/yoga/YGNode.h new file mode 100644 index 00000000..2967a1e2 --- /dev/null +++ b/doric-Qt/doric/yoga/YGNode.h @@ -0,0 +1,323 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include "Bitfield.h" +#include "CompactValue.h" +#include "YGConfig.h" +#include "YGLayout.h" +#include "YGStyle.h" +#include "YGMacros.h" +#include "Yoga-internal.h" + +YGConfigRef YGConfigGetDefault(); + +struct YOGA_EXPORT YGNode { + using MeasureWithContextFn = + YGSize (*)(YGNode*, float, YGMeasureMode, float, YGMeasureMode, void*); + using BaselineWithContextFn = float (*)(YGNode*, float, float, void*); + using PrintWithContextFn = void (*)(YGNode*, void*); + +private: + static constexpr size_t hasNewLayout_ = 0; + static constexpr size_t isReferenceBaseline_ = 1; + static constexpr size_t isDirty_ = 2; + static constexpr size_t nodeType_ = 3; + static constexpr size_t measureUsesContext_ = 4; + static constexpr size_t baselineUsesContext_ = 5; + static constexpr size_t printUsesContext_ = 6; + static constexpr size_t useWebDefaults_ = 7; + + void* context_ = nullptr; + using Flags = facebook::yoga:: + Bitfield; + Flags flags_ = + {true, false, false, YGNodeTypeDefault, false, false, false, false}; + uint8_t reserved_ = 0; + union { + YGMeasureFunc noContext; + MeasureWithContextFn withContext; + } measure_ = {nullptr}; + union { + YGBaselineFunc noContext; + BaselineWithContextFn withContext; + } baseline_ = {nullptr}; + union { + YGPrintFunc noContext; + PrintWithContextFn withContext; + } print_ = {nullptr}; + YGDirtiedFunc dirtied_ = nullptr; + YGStyle style_ = {}; + YGLayout layout_ = {}; + uint32_t lineIndex_ = 0; + YGNodeRef owner_ = nullptr; + YGVector children_ = {}; + YGConfigRef config_; + std::array resolvedDimensions_ = { + {YGValueUndefined, YGValueUndefined}}; + + YGFloatOptional relativePosition( + const YGFlexDirection axis, + const float axisSize) const; + + void setMeasureFunc(decltype(measure_)); + void setBaselineFunc(decltype(baseline_)); + + void useWebDefaults() { + flags_.at() = true; + style_.flexDirection() = YGFlexDirectionRow; + style_.alignContent() = YGAlignStretch; + } + + // DANGER DANGER DANGER! + // If the the node assigned to has children, we'd either have to deallocate + // them (potentially incorrect) or ignore them (danger of leaks). Only ever + // use this after checking that there are no children. + // DO NOT CHANGE THE VISIBILITY OF THIS METHOD! + YGNode& operator=(YGNode&&) = default; + + using CompactValue = facebook::yoga::detail::CompactValue; + +public: + YGNode() : YGNode{YGConfigGetDefault()} {} + explicit YGNode(const YGConfigRef config) : config_{config} { + if (config->useWebDefaults) { + useWebDefaults(); + } + }; + ~YGNode() = default; // cleanup of owner/children relationships in YGNodeFree + + YGNode(YGNode&&); + + // Does not expose true value semantics, as children are not cloned eagerly. + // Should we remove this? + YGNode(const YGNode& node) = default; + + // for RB fabric + YGNode(const YGNode& node, YGConfigRef config); + + // assignment means potential leaks of existing children, or alternatively + // freeing unowned memory, double free, or freeing stack memory. + YGNode& operator=(const YGNode&) = delete; + + // Getters + void* getContext() const { return context_; } + + uint8_t& reserved() { return reserved_; } + uint8_t reserved() const { return reserved_; } + + void print(void*); + + bool getHasNewLayout() const { return flags_.at(); } + + YGNodeType getNodeType() const { return flags_.at(); } + + bool hasMeasureFunc() const noexcept { return measure_.noContext != nullptr; } + + YGSize measure(float, YGMeasureMode, float, YGMeasureMode, void*); + + bool hasBaselineFunc() const noexcept { + return baseline_.noContext != nullptr; + } + + float baseline(float width, float height, void* layoutContext); + + YGDirtiedFunc getDirtied() const { return dirtied_; } + + // For Performance reasons passing as reference. + YGStyle& getStyle() { return style_; } + + const YGStyle& getStyle() const { return style_; } + + // For Performance reasons passing as reference. + YGLayout& getLayout() { return layout_; } + + const YGLayout& getLayout() const { return layout_; } + + uint32_t getLineIndex() const { return lineIndex_; } + + bool isReferenceBaseline() { return flags_.at(); } + + // returns the YGNodeRef that owns this YGNode. An owner is used to identify + // the YogaTree that a YGNode belongs to. This method will return the parent + // of the YGNode when a YGNode only belongs to one YogaTree or nullptr when + // the YGNode is shared between two or more YogaTrees. + YGNodeRef getOwner() const { return owner_; } + + // Deprecated, use getOwner() instead. + YGNodeRef getParent() const { return getOwner(); } + + const YGVector& getChildren() const { return children_; } + + // Applies a callback to all children, after cloning them if they are not + // owned. + template + void iterChildrenAfterCloningIfNeeded(T callback, void* cloneContext) { + int i = 0; + for (YGNodeRef& child : children_) { + if (child->getOwner() != this) { + child = config_->cloneNode(child, this, i, cloneContext); + child->setOwner(this); + } + i += 1; + + callback(child, cloneContext); + } + } + + YGNodeRef getChild(uint32_t index) const { return children_.at(index); } + + YGConfigRef getConfig() const { return config_; } + + bool isDirty() const { return flags_.at(); } + + std::array getResolvedDimensions() const { + return resolvedDimensions_; + } + + YGValue getResolvedDimension(int index) const { + return resolvedDimensions_[index]; + } + + // Methods related to positions, margin, padding and border + YGFloatOptional getLeadingPosition( + const YGFlexDirection axis, + const float axisSize) const; + bool isLeadingPositionDefined(const YGFlexDirection axis) const; + bool isTrailingPosDefined(const YGFlexDirection axis) const; + YGFloatOptional getTrailingPosition( + const YGFlexDirection axis, + const float axisSize) const; + YGFloatOptional getLeadingMargin( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingMargin( + const YGFlexDirection axis, + const float widthSize) const; + float getLeadingBorder(const YGFlexDirection flexDirection) const; + float getTrailingBorder(const YGFlexDirection flexDirection) const; + YGFloatOptional getLeadingPadding( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingPadding( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getLeadingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getTrailingPaddingAndBorder( + const YGFlexDirection axis, + const float widthSize) const; + YGFloatOptional getMarginForAxis( + const YGFlexDirection axis, + const float widthSize) const; + // Setters + + void setContext(void* context) { context_ = context; } + + void setPrintFunc(YGPrintFunc printFunc) { + print_.noContext = printFunc; + flags_.at() = false; + } + void setPrintFunc(PrintWithContextFn printFunc) { + print_.withContext = printFunc; + flags_.at() = true; + } + void setPrintFunc(std::nullptr_t) { setPrintFunc(YGPrintFunc{nullptr}); } + + void setHasNewLayout(bool hasNewLayout) { + flags_.at() = hasNewLayout; + } + + void setNodeType(YGNodeType nodeType) { flags_.at() = nodeType; } + + void setMeasureFunc(YGMeasureFunc measureFunc); + void setMeasureFunc(MeasureWithContextFn); + void setMeasureFunc(std::nullptr_t) { + return setMeasureFunc(YGMeasureFunc{nullptr}); + } + + void setBaselineFunc(YGBaselineFunc baseLineFunc) { + flags_.at() = false; + baseline_.noContext = baseLineFunc; + } + void setBaselineFunc(BaselineWithContextFn baseLineFunc) { + flags_.at() = true; + baseline_.withContext = baseLineFunc; + } + void setBaselineFunc(std::nullptr_t) { + return setBaselineFunc(YGBaselineFunc{nullptr}); + } + + void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) { dirtied_ = dirtiedFunc; } + + void setStyle(const YGStyle& style) { style_ = style; } + + void setLayout(const YGLayout& layout) { layout_ = layout; } + + void setLineIndex(uint32_t lineIndex) { lineIndex_ = lineIndex; } + + void setIsReferenceBaseline(bool isReferenceBaseline) { + flags_.at() = isReferenceBaseline; + } + + void setOwner(YGNodeRef owner) { owner_ = owner; } + + void setChildren(const YGVector& children) { children_ = children; } + + // TODO: rvalue override for setChildren + + YG_DEPRECATED void setConfig(YGConfigRef config) { config_ = config; } + + void setDirty(bool isDirty); + void setLayoutLastOwnerDirection(YGDirection direction); + void setLayoutComputedFlexBasis(const YGFloatOptional computedFlexBasis); + void setLayoutComputedFlexBasisGeneration( + uint32_t computedFlexBasisGeneration); + void setLayoutMeasuredDimension(float measuredDimension, int index); + void setLayoutHadOverflow(bool hadOverflow); + void setLayoutDimension(float dimension, int index); + void setLayoutDirection(YGDirection direction); + void setLayoutMargin(float margin, int index); + void setLayoutBorder(float border, int index); + void setLayoutPadding(float padding, int index); + void setLayoutPosition(float position, int index); + void setPosition( + const YGDirection direction, + const float mainSize, + const float crossSize, + const float ownerWidth); + void setLayoutDoesLegacyFlagAffectsLayout(bool doesLegacyFlagAffectsLayout); + void setLayoutDidUseLegacyFlag(bool didUseLegacyFlag); + void markDirtyAndPropogateDownwards(); + + // Other methods + YGValue marginLeadingValue(const YGFlexDirection axis) const; + YGValue marginTrailingValue(const YGFlexDirection axis) const; + YGValue resolveFlexBasisPtr() const; + void resolveDimension(); + YGDirection resolveDirection(const YGDirection ownerDirection); + void clearChildren(); + /// Replaces the occurrences of oldChild with newChild + void replaceChild(YGNodeRef oldChild, YGNodeRef newChild); + void replaceChild(YGNodeRef child, uint32_t index); + void insertChild(YGNodeRef child, uint32_t index); + /// Removes the first occurrence of child + bool removeChild(YGNodeRef child); + void removeChild(uint32_t index); + + void cloneChildrenIfNeeded(void*); + void markDirtyAndPropogate(); + float resolveFlexGrow() const; + float resolveFlexShrink() const; + bool isNodeFlexible(); + bool didUseLegacyFlag(); + bool isLayoutTreeEqualToNode(const YGNode& node) const; + void reset(); +}; diff --git a/doric-Qt/doric/yoga/YGNodePrint.cpp b/doric-Qt/doric/yoga/YGNodePrint.cpp new file mode 100644 index 00000000..26efa485 --- /dev/null +++ b/doric-Qt/doric/yoga/YGNodePrint.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef DEBUG +#include "YGNodePrint.h" +#include +#include "YGEnums.h" +#include "YGNode.h" +#include "Yoga-internal.h" +#include "Utils.h" + +namespace facebook { +namespace yoga { +typedef std::string string; + +static void indent(string& base, uint32_t level) { + for (uint32_t i = 0; i < level; ++i) { + base.append(" "); + } +} + +static bool areFourValuesEqual(const YGStyle::Edges& four) { + return YGValueEqual(four[0], four[1]) && YGValueEqual(four[0], four[2]) && + YGValueEqual(four[0], four[3]); +} + +static void appendFormatedString(string& str, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + va_list argsCopy; + va_copy(argsCopy, args); + std::vector buf(1 + vsnprintf(NULL, 0, fmt, args)); + va_end(args); + vsnprintf(buf.data(), buf.size(), fmt, argsCopy); + va_end(argsCopy); + string result = string(buf.begin(), buf.end() - 1); + str.append(result); +} + +static void appendFloatOptionalIfDefined( + string& base, + const string key, + const YGFloatOptional num) { + if (!num.isUndefined()) { + appendFormatedString(base, "%s: %g; ", key.c_str(), num.unwrap()); + } +} + +static void appendNumberIfNotUndefined( + string& base, + const string key, + const YGValue number) { + if (number.unit != YGUnitUndefined) { + if (number.unit == YGUnitAuto) { + base.append(key + ": auto; "); + } else { + string unit = number.unit == YGUnitPoint ? "px" : "%%"; + appendFormatedString( + base, "%s: %g%s; ", key.c_str(), number.value, unit.c_str()); + } + } +} + +static void appendNumberIfNotAuto( + string& base, + const string& key, + const YGValue number) { + if (number.unit != YGUnitAuto) { + appendNumberIfNotUndefined(base, key, number); + } +} + +static void appendNumberIfNotZero( + string& base, + const string& str, + const YGValue number) { + if (number.unit == YGUnitAuto) { + base.append(str + ": auto; "); + } else if (!YGFloatsEqual(number.value, 0)) { + appendNumberIfNotUndefined(base, str, number); + } +} + +static void appendEdges( + string& base, + const string& key, + const YGStyle::Edges& edges) { + if (areFourValuesEqual(edges)) { + appendNumberIfNotZero(base, key, edges[YGEdgeLeft]); + } else { + for (int edge = YGEdgeLeft; edge != YGEdgeAll; ++edge) { + string str = key + "-" + YGEdgeToString(static_cast(edge)); + appendNumberIfNotZero(base, str, edges[edge]); + } + } +} + +static void appendEdgeIfNotUndefined( + string& base, + const string& str, + const YGStyle::Edges& edges, + const YGEdge edge) { + appendNumberIfNotUndefined( + base, + str, + YGComputedEdgeValue(edges, edge, detail::CompactValue::ofUndefined())); +} + +void YGNodeToString( + std::string& str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level) { + indent(str, level); + appendFormatedString(str, "
getLayout().dimensions[YGDimensionWidth]); + appendFormatedString( + str, "height: %g; ", node->getLayout().dimensions[YGDimensionHeight]); + appendFormatedString( + str, "top: %g; ", node->getLayout().position[YGEdgeTop]); + appendFormatedString( + str, "left: %g;", node->getLayout().position[YGEdgeLeft]); + appendFormatedString(str, "\" "); + } + + if (options & YGPrintOptionsStyle) { + appendFormatedString(str, "style=\""); + const auto& style = node->getStyle(); + if (style.flexDirection() != YGNode().getStyle().flexDirection()) { + appendFormatedString( + str, + "flex-direction: %s; ", + YGFlexDirectionToString(style.flexDirection())); + } + if (style.justifyContent() != YGNode().getStyle().justifyContent()) { + appendFormatedString( + str, + "justify-content: %s; ", + YGJustifyToString(style.justifyContent())); + } + if (style.alignItems() != YGNode().getStyle().alignItems()) { + appendFormatedString( + str, "align-items: %s; ", YGAlignToString(style.alignItems())); + } + if (style.alignContent() != YGNode().getStyle().alignContent()) { + appendFormatedString( + str, "align-content: %s; ", YGAlignToString(style.alignContent())); + } + if (style.alignSelf() != YGNode().getStyle().alignSelf()) { + appendFormatedString( + str, "align-self: %s; ", YGAlignToString(style.alignSelf())); + } + appendFloatOptionalIfDefined(str, "flex-grow", style.flexGrow()); + appendFloatOptionalIfDefined(str, "flex-shrink", style.flexShrink()); + appendNumberIfNotAuto(str, "flex-basis", style.flexBasis()); + appendFloatOptionalIfDefined(str, "flex", style.flex()); + + if (style.flexWrap() != YGNode().getStyle().flexWrap()) { + appendFormatedString( + str, "flex-wrap: %s; ", YGWrapToString(style.flexWrap())); + } + + if (style.overflow() != YGNode().getStyle().overflow()) { + appendFormatedString( + str, "overflow: %s; ", YGOverflowToString(style.overflow())); + } + + if (style.display() != YGNode().getStyle().display()) { + appendFormatedString( + str, "display: %s; ", YGDisplayToString(style.display())); + } + appendEdges(str, "margin", style.margin()); + appendEdges(str, "padding", style.padding()); + appendEdges(str, "border", style.border()); + + appendNumberIfNotAuto(str, "width", style.dimensions()[YGDimensionWidth]); + appendNumberIfNotAuto(str, "height", style.dimensions()[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "max-width", style.maxDimensions()[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "max-height", style.maxDimensions()[YGDimensionHeight]); + appendNumberIfNotAuto( + str, "min-width", style.minDimensions()[YGDimensionWidth]); + appendNumberIfNotAuto( + str, "min-height", style.minDimensions()[YGDimensionHeight]); + + if (style.positionType() != YGNode().getStyle().positionType()) { + appendFormatedString( + str, "position: %s; ", YGPositionTypeToString(style.positionType())); + } + + appendEdgeIfNotUndefined(str, "left", style.position(), YGEdgeLeft); + appendEdgeIfNotUndefined(str, "right", style.position(), YGEdgeRight); + appendEdgeIfNotUndefined(str, "top", style.position(), YGEdgeTop); + appendEdgeIfNotUndefined(str, "bottom", style.position(), YGEdgeBottom); + appendFormatedString(str, "\" "); + + if (node->hasMeasureFunc()) { + appendFormatedString(str, "has-custom-measure=\"true\""); + } + } + appendFormatedString(str, ">"); + + const uint32_t childCount = static_cast(node->getChildren().size()); + if (options & YGPrintOptionsChildren && childCount > 0) { + for (uint32_t i = 0; i < childCount; i++) { + appendFormatedString(str, "\n"); + YGNodeToString(str, YGNodeGetChild(node, i), options, level + 1); + } + appendFormatedString(str, "\n"); + indent(str, level); + } + appendFormatedString(str, "
"); +} +} // namespace yoga +} // namespace facebook +#endif diff --git a/doric-Qt/doric/yoga/YGNodePrint.h b/doric-Qt/doric/yoga/YGNodePrint.h new file mode 100644 index 00000000..3db504b4 --- /dev/null +++ b/doric-Qt/doric/yoga/YGNodePrint.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#ifdef DEBUG +#pragma once +#include + +#include "Yoga.h" + +namespace facebook { +namespace yoga { + +void YGNodeToString( + std::string& str, + YGNodeRef node, + YGPrintOptions options, + uint32_t level); + +} // namespace yoga +} // namespace facebook +#endif diff --git a/doric-Qt/doric/yoga/YGStyle.cpp b/doric-Qt/doric/yoga/YGStyle.cpp new file mode 100644 index 00000000..e8033bdf --- /dev/null +++ b/doric-Qt/doric/yoga/YGStyle.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGStyle.h" +#include "Utils.h" + +// Yoga specific properties, not compatible with flexbox specification +bool operator==(const YGStyle& lhs, const YGStyle& rhs) { + bool areNonFloatValuesEqual = lhs.direction() == rhs.direction() && + lhs.flexDirection() == rhs.flexDirection() && + lhs.justifyContent() == rhs.justifyContent() && + lhs.alignContent() == rhs.alignContent() && + lhs.alignItems() == rhs.alignItems() && + lhs.alignSelf() == rhs.alignSelf() && + lhs.positionType() == rhs.positionType() && + lhs.flexWrap() == rhs.flexWrap() && lhs.overflow() == rhs.overflow() && + lhs.display() == rhs.display() && + YGValueEqual(lhs.flexBasis(), rhs.flexBasis()) && + lhs.margin() == rhs.margin() && lhs.position() == rhs.position() && + lhs.padding() == rhs.padding() && lhs.border() == rhs.border() && + lhs.dimensions() == rhs.dimensions() && + lhs.minDimensions() == rhs.minDimensions() && + lhs.maxDimensions() == rhs.maxDimensions(); + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flex().isUndefined() == rhs.flex().isUndefined(); + if (areNonFloatValuesEqual && !lhs.flex().isUndefined() && + !rhs.flex().isUndefined()) { + areNonFloatValuesEqual = areNonFloatValuesEqual && lhs.flex() == rhs.flex(); + } + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flexGrow().isUndefined() == rhs.flexGrow().isUndefined(); + if (areNonFloatValuesEqual && !lhs.flexGrow().isUndefined()) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.flexGrow() == rhs.flexGrow(); + } + + areNonFloatValuesEqual = areNonFloatValuesEqual && + lhs.flexShrink().isUndefined() == rhs.flexShrink().isUndefined(); + if (areNonFloatValuesEqual && !rhs.flexShrink().isUndefined()) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.flexShrink() == rhs.flexShrink(); + } + + if (!(lhs.aspectRatio().isUndefined() && rhs.aspectRatio().isUndefined())) { + areNonFloatValuesEqual = + areNonFloatValuesEqual && lhs.aspectRatio() == rhs.aspectRatio(); + } + + return areNonFloatValuesEqual; +} diff --git a/doric-Qt/doric/yoga/YGStyle.h b/doric-Qt/doric/yoga/YGStyle.h new file mode 100644 index 00000000..b066b346 --- /dev/null +++ b/doric-Qt/doric/yoga/YGStyle.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include +#include +#include "Bitfield.h" +#include "CompactValue.h" +#include "YGEnums.h" +#include "YGFloatOptional.h" +#include "Yoga-internal.h" +#include "Yoga.h" + +class YOGA_EXPORT YGStyle { + template + using Values = + facebook::yoga::detail::Values()>; + using CompactValue = facebook::yoga::detail::CompactValue; + +public: + using Dimensions = Values; + using Edges = Values; + + template + struct Ref { + YGStyle& style; + operator T() const { return style.*Prop; } + Ref& operator=(T value) { + style.*Prop = value; + return *this; + } + }; + + template YGStyle::*Prop> + struct IdxRef { + struct Ref { + YGStyle& style; + Idx idx; + operator CompactValue() const { return (style.*Prop)[idx]; } + operator YGValue() const { return (style.*Prop)[idx]; } + Ref& operator=(CompactValue value) { + (style.*Prop)[idx] = value; + return *this; + } + }; + + YGStyle& style; + IdxRef& operator=(const Values& values) { + style.*Prop = values; + return *this; + } + operator const Values&() const { return style.*Prop; } + Ref operator[](Idx idx) { return {style, idx}; } + CompactValue operator[](Idx idx) const { return (style.*Prop)[idx]; } + }; + + YGStyle() = default; + ~YGStyle() = default; + +private: + static constexpr size_t directionIdx = 0; + static constexpr size_t flexDirectionIdx = 1; + static constexpr size_t justifyContentIdx = 2; + static constexpr size_t alignContentIdx = 3; + static constexpr size_t alignItemsIdx = 4; + static constexpr size_t alignSelfIdx = 5; + static constexpr size_t positionTypeIdx = 6; + static constexpr size_t flexWrapIdx = 7; + static constexpr size_t overflowIdx = 8; + static constexpr size_t displayIdx = 9; + using Flags = facebook::yoga::Bitfield< + uint32_t, + YGDirection, + YGFlexDirection, + YGJustify, + YGAlign, + YGAlign, + YGAlign, + YGPositionType, + YGWrap, + YGOverflow, + YGDisplay>; + + Flags flags_ = {YGDirectionInherit, + YGFlexDirectionColumn, + YGJustifyFlexStart, + YGAlignFlexStart, + YGAlignStretch, + YGAlignAuto, + YGPositionTypeRelative, + YGWrapNoWrap, + YGOverflowVisible, + YGDisplayFlex}; + YGFloatOptional flex_ = {}; + YGFloatOptional flexGrow_ = {}; + YGFloatOptional flexShrink_ = {}; + CompactValue flexBasis_ = CompactValue::ofAuto(); + Edges margin_ = {}; + Edges position_ = {}; + Edges padding_ = {}; + Edges border_ = {}; + Dimensions dimensions_{CompactValue::ofAuto()}; + Dimensions minDimensions_ = {}; + Dimensions maxDimensions_ = {}; + // Yoga specific properties, not compatible with flexbox specification + YGFloatOptional aspectRatio_ = {}; + +public: + // for library users needing a type + using ValueRepr = std::remove_reference::type; + + YGDirection direction() const { return flags_.at(); } + Flags::Ref direction() { return flags_.at(); } + + YGFlexDirection flexDirection() const { + return flags_.at(); + } + Flags::Ref flexDirection() { + return flags_.at(); + } + + YGJustify justifyContent() const { return flags_.at(); } + Flags::Ref justifyContent() { + return flags_.at(); + } + + YGAlign alignContent() const { return flags_.at(); } + Flags::Ref alignContent() { + return flags_.at(); + } + + YGAlign alignItems() const { return flags_.at(); } + Flags::Ref alignItems() { return flags_.at(); } + + YGAlign alignSelf() const { return flags_.at(); } + Flags::Ref alignSelf() { return flags_.at(); } + + YGPositionType positionType() const { return flags_.at(); } + Flags::Ref positionType() { + return flags_.at(); + } + + YGWrap flexWrap() const { return flags_.at(); } + Flags::Ref flexWrap() { return flags_.at(); } + + YGOverflow overflow() const { return flags_.at(); } + Flags::Ref overflow() { return flags_.at(); } + + YGDisplay display() const { return flags_.at(); } + Flags::Ref display() { return flags_.at(); } + + YGFloatOptional flex() const { return flex_; } + Ref flex() { return {*this}; } + + YGFloatOptional flexGrow() const { return flexGrow_; } + Ref flexGrow() { return {*this}; } + + YGFloatOptional flexShrink() const { return flexShrink_; } + Ref flexShrink() { return {*this}; } + + CompactValue flexBasis() const { return flexBasis_; } + Ref flexBasis() { return {*this}; } + + const Edges& margin() const { return margin_; } + IdxRef margin() { return {*this}; } + + const Edges& position() const { return position_; } + IdxRef position() { return {*this}; } + + const Edges& padding() const { return padding_; } + IdxRef padding() { return {*this}; } + + const Edges& border() const { return border_; } + IdxRef border() { return {*this}; } + + const Dimensions& dimensions() const { return dimensions_; } + IdxRef dimensions() { return {*this}; } + + const Dimensions& minDimensions() const { return minDimensions_; } + IdxRef minDimensions() { + return {*this}; + } + + const Dimensions& maxDimensions() const { return maxDimensions_; } + IdxRef maxDimensions() { + return {*this}; + } + + // Yoga specific properties, not compatible with flexbox specification + YGFloatOptional aspectRatio() const { return aspectRatio_; } + Ref aspectRatio() { return {*this}; } +}; + +YOGA_EXPORT bool operator==(const YGStyle& lhs, const YGStyle& rhs); +YOGA_EXPORT inline bool operator!=(const YGStyle& lhs, const YGStyle& rhs) { + return !(lhs == rhs); +} diff --git a/doric-Qt/doric/yoga/YGValue.cpp b/doric-Qt/doric/yoga/YGValue.cpp new file mode 100644 index 00000000..37383a55 --- /dev/null +++ b/doric-Qt/doric/yoga/YGValue.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "YGValue.h" + +const YGValue YGValueZero = {0, YGUnitPoint}; +const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined}; +const YGValue YGValueAuto = {YGUndefined, YGUnitAuto}; diff --git a/doric-Qt/doric/yoga/YGValue.h b/doric-Qt/doric/yoga/YGValue.h new file mode 100644 index 00000000..aaa10c3f --- /dev/null +++ b/doric-Qt/doric/yoga/YGValue.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include "YGEnums.h" +#include "YGMacros.h" + +YG_EXTERN_C_BEGIN + +// Not defined in MSVC++ +#ifndef NAN +static const uint32_t __nan = 0x7fc00000; +#define NAN (*(const float*) __nan) +#endif + +#define YGUndefined NAN + +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + +YOGA_EXPORT extern const YGValue YGValueAuto; +YOGA_EXPORT extern const YGValue YGValueUndefined; +YOGA_EXPORT extern const YGValue YGValueZero; + +YG_EXTERN_C_END + +#ifdef __cplusplus + +inline bool operator==(const YGValue& lhs, const YGValue& rhs) { + if (lhs.unit != rhs.unit) { + return false; + } + + switch (lhs.unit) { + case YGUnitUndefined: + case YGUnitAuto: + return true; + case YGUnitPoint: + case YGUnitPercent: + return lhs.value == rhs.value; + } + + return false; +} + +inline bool operator!=(const YGValue& lhs, const YGValue& rhs) { + return !(lhs == rhs); +} + +inline YGValue operator-(const YGValue& value) { + return {-value.value, value.unit}; +} + +namespace facebook { +namespace yoga { +namespace literals { + +inline YGValue operator"" _pt(long double value) { + return YGValue{static_cast(value), YGUnitPoint}; +} +inline YGValue operator"" _pt(unsigned long long value) { + return operator"" _pt(static_cast(value)); +} + +inline YGValue operator"" _percent(long double value) { + return YGValue{static_cast(value), YGUnitPercent}; +} +inline YGValue operator"" _percent(unsigned long long value) { + return operator"" _percent(static_cast(value)); +} + +} // namespace literals +} // namespace yoga +} // namespace facebook + +#endif diff --git a/doric-Qt/doric/yoga/Yoga-internal.h b/doric-Qt/doric/yoga/Yoga-internal.h new file mode 100644 index 00000000..0b3368a0 --- /dev/null +++ b/doric-Qt/doric/yoga/Yoga-internal.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once +#include +#include +#include +#include +#include "CompactValue.h" +#include "Yoga.h" + +using YGVector = std::vector; + +YG_EXTERN_C_BEGIN + +void YGNodeCalculateLayoutWithContext( + YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection ownerDirection, + void* layoutContext); + +YG_EXTERN_C_END + +namespace facebook { +namespace yoga { + +inline bool isUndefined(float value) { + return std::isnan(value); +} + +} // namespace yoga +} // namespace facebook + +using namespace facebook; + +extern const std::array trailing; +extern const std::array leading; +extern const YGValue YGValueUndefined; +extern const YGValue YGValueAuto; +extern const YGValue YGValueZero; + +struct YGCachedMeasurement { + float availableWidth; + float availableHeight; + YGMeasureMode widthMeasureMode; + YGMeasureMode heightMeasureMode; + + float computedWidth; + float computedHeight; + + YGCachedMeasurement() + : availableWidth(0), + availableHeight(0), + widthMeasureMode((YGMeasureMode) -1), + heightMeasureMode((YGMeasureMode) -1), + computedWidth(-1), + computedHeight(-1) {} + + bool operator==(YGCachedMeasurement measurement) const { + bool isEqual = widthMeasureMode == measurement.widthMeasureMode && + heightMeasureMode == measurement.heightMeasureMode; + + if (!yoga::isUndefined(availableWidth) || + !yoga::isUndefined(measurement.availableWidth)) { + isEqual = isEqual && availableWidth == measurement.availableWidth; + } + if (!yoga::isUndefined(availableHeight) || + !yoga::isUndefined(measurement.availableHeight)) { + isEqual = isEqual && availableHeight == measurement.availableHeight; + } + if (!yoga::isUndefined(computedWidth) || + !yoga::isUndefined(measurement.computedWidth)) { + isEqual = isEqual && computedWidth == measurement.computedWidth; + } + if (!yoga::isUndefined(computedHeight) || + !yoga::isUndefined(measurement.computedHeight)) { + isEqual = isEqual && computedHeight == measurement.computedHeight; + } + + return isEqual; + } +}; + +// This value was chosen based on empirical data: +// 98% of analyzed layouts require less than 8 entries. +#define YG_MAX_CACHED_RESULT_COUNT 8 + +namespace facebook { +namespace yoga { +namespace detail { + +template +class Values { +private: + std::array values_; + +public: + Values() = default; + explicit Values(const YGValue& defaultValue) noexcept { + values_.fill(defaultValue); + } + + const CompactValue& operator[](size_t i) const noexcept { return values_[i]; } + CompactValue& operator[](size_t i) noexcept { return values_[i]; } + + template + YGValue get() const noexcept { + return std::get(values_); + } + + template + void set(YGValue& value) noexcept { + std::get(values_) = value; + } + + template + void set(YGValue&& value) noexcept { + set(value); + } + + bool operator==(const Values& other) const noexcept { + for (size_t i = 0; i < Size; ++i) { + if (values_[i] != other.values_[i]) { + return false; + } + } + return true; + } + + Values& operator=(const Values& other) = default; +}; + +} // namespace detail +} // namespace yoga +} // namespace facebook + +static const float kDefaultFlexGrow = 0.0f; +static const float kDefaultFlexShrink = 0.0f; +static const float kWebDefaultFlexShrink = 1.0f; + +extern bool YGFloatsEqual(const float a, const float b); +extern facebook::yoga::detail::CompactValue YGComputedEdgeValue( + const facebook::yoga::detail::Values< + facebook::yoga::enums::count()>& edges, + YGEdge edge, + facebook::yoga::detail::CompactValue defaultValue); diff --git a/doric-Qt/doric/yoga/Yoga.cpp b/doric-Qt/doric/yoga/Yoga.cpp new file mode 100644 index 00000000..016ab0ac --- /dev/null +++ b/doric-Qt/doric/yoga/Yoga.cpp @@ -0,0 +1,4405 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "Yoga.h" +#include "log.h" +#include +#include +#include +#include +#include +#include "Utils.h" +#include "YGNode.h" +#include "YGNodePrint.h" +#include "Yoga-internal.h" +#include "event/event.h" +#ifdef _MSC_VER +#include + +/* define fmaxf if < VC12 */ +#if _MSC_VER < 1800 +__forceinline const float fmaxf(const float a, const float b) { + return (a > b) ? a : b; +} +#endif +#endif + +using namespace facebook::yoga; +using detail::Log; + +#ifdef ANDROID +static int YGAndroidLog( + const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args); +#else +static int YGDefaultLog( + const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args); +#endif + +#ifdef ANDROID +#include +static int YGAndroidLog( + const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args) { + int androidLevel = YGLogLevelDebug; + switch (level) { + case YGLogLevelFatal: + androidLevel = ANDROID_LOG_FATAL; + break; + case YGLogLevelError: + androidLevel = ANDROID_LOG_ERROR; + break; + case YGLogLevelWarn: + androidLevel = ANDROID_LOG_WARN; + break; + case YGLogLevelInfo: + androidLevel = ANDROID_LOG_INFO; + break; + case YGLogLevelDebug: + androidLevel = ANDROID_LOG_DEBUG; + break; + case YGLogLevelVerbose: + androidLevel = ANDROID_LOG_VERBOSE; + break; + } + const int result = __android_log_vprint(androidLevel, "yoga", format, args); + return result; +} +#else +#define YG_UNUSED(x) (void) (x); + +static int YGDefaultLog( + const YGConfigRef config, + const YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args) { + YG_UNUSED(config); + YG_UNUSED(node); + switch (level) { + case YGLogLevelError: + case YGLogLevelFatal: + return vfprintf(stderr, format, args); + case YGLogLevelWarn: + case YGLogLevelInfo: + case YGLogLevelDebug: + case YGLogLevelVerbose: + default: + return vprintf(format, args); + } +} + +#undef YG_UNUSED +#endif + +YOGA_EXPORT bool YGFloatIsUndefined(const float value) { + return facebook::yoga::isUndefined(value); +} + +detail::CompactValue YGComputedEdgeValue( + const YGStyle::Edges& edges, + YGEdge edge, + detail::CompactValue defaultValue) { + if (!edges[edge].isUndefined()) { + return edges[edge]; + } + + if ((edge == YGEdgeTop || edge == YGEdgeBottom) && + !edges[YGEdgeVertical].isUndefined()) { + return edges[YGEdgeVertical]; + } + + if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || + edge == YGEdgeEnd) && + !edges[YGEdgeHorizontal].isUndefined()) { + return edges[YGEdgeHorizontal]; + } + + if (!edges[YGEdgeAll].isUndefined()) { + return edges[YGEdgeAll]; + } + + if (edge == YGEdgeStart || edge == YGEdgeEnd) { + return detail::CompactValue::ofUndefined(); + } + + return defaultValue; +} + +YOGA_EXPORT void* YGNodeGetContext(YGNodeRef node) { + return node->getContext(); +} + +YOGA_EXPORT void YGNodeSetContext(YGNodeRef node, void* context) { + return node->setContext(context); +} + +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->printTree = 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 bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) { + return node->didUseLegacyFlag(); +} + +YOGA_EXPORT void YGNodeMarkDirtyAndPropogateToDescendants( + const YGNodeRef node) { + return node->markDirtyAndPropogateDownwards(); +} + +int32_t gConfigInstanceCount = 0; + +YOGA_EXPORT WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) { + const YGNodeRef node = new YGNode{config}; + YGAssertWithConfig( + config, node != nullptr, "Could not allocate memory for node"); + Event::publish(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(node, {node->getConfig()}); + node->setOwner(nullptr); + return node; +} + +static YGConfigRef YGConfigClone(const YGConfig& oldConfig) { + const YGConfigRef config = new YGConfig(oldConfig); + YGAssert(config != nullptr, "Could not allocate memory for config"); + if (config == nullptr) { + abort(); + } + gConfigInstanceCount++; + return config; +} + +static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) { + auto config = YGConfigClone(*oldNode->getConfig()); + auto node = new YGNode{*oldNode, config}; + node->setOwner(nullptr); + Event::publish(node, {node->getConfig()}); + + YGVector vec = YGVector(); + vec.reserve(oldNode->getChildren().size()); + YGNodeRef childNode = nullptr; + for (auto* item : oldNode->getChildren()) { + childNode = YGNodeDeepClone(item); + childNode->setOwner(node); + vec.push_back(childNode); + } + node->setChildren(vec); + + 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(); + Event::publish(node, {node->getConfig()}); + delete node; +} + +static void YGConfigFreeRecursive(const YGNodeRef root) { + if (root->getConfig() != nullptr) { + gConfigInstanceCount--; + delete root->getConfig(); + } + // Delete configs recursively for childrens + for (auto* child : root->getChildren()) { + YGConfigFreeRecursive(child); + } +} + +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) { + // Don't free shared nodes that we don't own. + skipped += 1; + } else { + YGNodeRemoveChild(root, child); + YGNodeFreeRecursive(child); + } + } + if (cleanup != nullptr) { + cleanup(root); + } + YGNodeFree(root); +} + +YOGA_EXPORT void YGNodeFreeRecursive(const YGNodeRef root) { + return YGNodeFreeRecursiveWithCleanupFunc(root, nullptr); +} + +YOGA_EXPORT void YGNodeReset(YGNodeRef node) { + node->reset(); +} + +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->markDirtyAndPropogate(); + } +} + +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->markDirtyAndPropogate(); +} + +YOGA_EXPORT void YGNodeRemoveChild( + const YGNodeRef owner, + const YGNodeRef excludedChild) { + if (YGNodeGetChildCount(owner) == 0) { + // This is an empty set. Nothing to remove. + return; + } + + // Children may be shared between parents, which is indicated by not having an + // owner. We only want to reset the child completely if it is owned + // exclusively by one node. + auto childOwner = excludedChild->getOwner(); + if (owner->removeChild(excludedChild)) { + if (owner == childOwner) { + excludedChild->setLayout({}); // layout is no longer valid + excludedChild->setOwner(nullptr); + } + owner->markDirtyAndPropogate(); + } +} + +YOGA_EXPORT void YGNodeRemoveAllChildren(const YGNodeRef owner) { + const uint32_t childCount = YGNodeGetChildCount(owner); + if (childCount == 0) { + // This is an empty set already. Nothing to do. + return; + } + const YGNodeRef firstChild = YGNodeGetChild(owner, 0); + if (firstChild->getOwner() == owner) { + // If the first child has this node as its owner, we assume that this child + // set is unique. + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef oldChild = YGNodeGetChild(owner, i); + oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid + oldChild->setOwner(nullptr); + } + owner->clearChildren(); + owner->markDirtyAndPropogate(); + return; + } + // Otherwise, we are not the owner of the child set. We don't have to do + // anything to clear it. + owner->setChildren(YGVector()); + owner->markDirtyAndPropogate(); +} + +static void YGNodeSetChildrenInternal( + YGNodeRef const owner, + const std::vector& children) { + if (!owner) { + return; + } + if (children.size() == 0) { + if (YGNodeGetChildCount(owner) > 0) { + for (YGNodeRef const child : owner->getChildren()) { + child->setLayout(YGLayout()); + child->setOwner(nullptr); + } + owner->setChildren(YGVector()); + owner->markDirtyAndPropogate(); + } + } else { + if (YGNodeGetChildCount(owner) > 0) { + for (YGNodeRef const oldChild : owner->getChildren()) { + // Our new children may have nodes in common with the old children. We + // don't reset these common nodes. + if (std::find(children.begin(), children.end(), oldChild) == + children.end()) { + oldChild->setLayout(YGLayout()); + oldChild->setOwner(nullptr); + } + } + } + owner->setChildren(children); + for (YGNodeRef child : children) { + child->setOwner(owner); + } + owner->markDirtyAndPropogate(); + } +} + +YOGA_EXPORT void YGNodeSetChildren( + const YGNodeRef owner, + const YGNodeRef c[], + const uint32_t count) { + const YGVector children = {c, c + count}; + YGNodeSetChildrenInternal(owner, children); +} + +YOGA_EXPORT void YGNodeSetChildren( + YGNodeRef const owner, + const std::vector& children) { + YGNodeSetChildrenInternal(owner, children); +} + +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(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->markDirtyAndPropogate(); +} + +YOGA_EXPORT void YGNodeCopyStyle( + const YGNodeRef dstNode, + const YGNodeRef srcNode) { + if (!(dstNode->getStyle() == srcNode->getStyle())) { + dstNode->setStyle(srcNode->getStyle()); + dstNode->markDirtyAndPropogate(); + } +} + +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 +void updateStyle( + YGNode* node, + T value, + NeedsUpdate&& needsUpdate, + Update&& update) { + if (needsUpdate(node->getStyle(), value)) { + update(node->getStyle(), value); + node->markDirtyAndPropogate(); + } +} + +template +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 +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; }); +} + +} // namespace + +// MSVC has trouble inferring the return type of pointer to member functions +// with const and non-const overloads, instead of preferring the non-const +// overload like clang and GCC. For the purposes of updateStyle(), we can help +// MSVC by specifying that return type explicitely. In combination with +// decltype, MSVC will prefer the non-const version. +#define MSVC_HINT(PROP) decltype(YGStyle{}.PROP()) + +YOGA_EXPORT void YGNodeStyleSetDirection( + const YGNodeRef node, + const YGDirection value) { + updateStyle(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( + 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( + 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( + 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(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(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( + 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(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(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(node, &YGStyle::display, display); +} +YOGA_EXPORT YGDisplay YGNodeStyleGetDisplay(const YGNodeConstRef node) { + return node->getStyle().display(); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) { + updateStyle(node, &YGStyle::flex, YGFloatOptional{flex}); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT float YGNodeStyleGetFlex(const YGNodeConstRef node) { + return node->getStyle().flex().isUndefined() + ? YGUndefined + : node->getStyle().flex().unwrap(); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT void YGNodeStyleSetFlexGrow( + const YGNodeRef node, + const float flexGrow) { + updateStyle( + node, &YGStyle::flexGrow, YGFloatOptional{flexGrow}); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT void YGNodeStyleSetFlexShrink( + const YGNodeRef node, + const float flexShrink) { + updateStyle( + node, &YGStyle::flexShrink, YGFloatOptional{flexShrink}); +} + +YOGA_EXPORT YGValue YGNodeStyleGetFlexBasis(const YGNodeConstRef node) { + YGValue flexBasis = node->getStyle().flexBasis(); + if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) { + // TODO(T26792433): Get rid off the use of YGUndefined at client side + flexBasis.value = YGUndefined; + } + return flexBasis; +} + +YOGA_EXPORT void YGNodeStyleSetFlexBasis( + const YGNodeRef node, + const float flexBasis) { + auto value = detail::CompactValue::ofMaybe(flexBasis); + updateStyle(node, &YGStyle::flexBasis, value); +} + +YOGA_EXPORT void YGNodeStyleSetFlexBasisPercent( + const YGNodeRef node, + const float flexBasisPercent) { + auto value = detail::CompactValue::ofMaybe(flexBasisPercent); + updateStyle(node, &YGStyle::flexBasis, value); +} + +YOGA_EXPORT void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) { + updateStyle( + node, &YGStyle::flexBasis, detail::CompactValue::ofAuto()); +} + +YOGA_EXPORT void YGNodeStyleSetPosition( + YGNodeRef node, + YGEdge edge, + float points) { + auto value = detail::CompactValue::ofMaybe(points); + updateIndexedStyleProp( + node, &YGStyle::position, edge, value); +} +YOGA_EXPORT void YGNodeStyleSetPositionPercent( + YGNodeRef node, + YGEdge edge, + float percent) { + auto value = detail::CompactValue::ofMaybe(percent); + updateIndexedStyleProp( + 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(points); + updateIndexedStyleProp( + node, &YGStyle::margin, edge, value); +} +YOGA_EXPORT void YGNodeStyleSetMarginPercent( + YGNodeRef node, + YGEdge edge, + float percent) { + auto value = detail::CompactValue::ofMaybe(percent); + updateIndexedStyleProp( + node, &YGStyle::margin, edge, value); +} +YOGA_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge) { + updateIndexedStyleProp( + 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(points); + updateIndexedStyleProp( + node, &YGStyle::padding, edge, value); +} +YOGA_EXPORT void YGNodeStyleSetPaddingPercent( + YGNodeRef node, + YGEdge edge, + float percent) { + auto value = detail::CompactValue::ofMaybe(percent); + updateIndexedStyleProp( + node, &YGStyle::padding, edge, value); +} +YOGA_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge) { + return node->getStyle().padding()[edge]; +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT void YGNodeStyleSetBorder( + const YGNodeRef node, + const YGEdge edge, + const float border) { + auto value = detail::CompactValue::ofMaybe(border); + updateIndexedStyleProp( + 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()) { + // TODO(T26792433): Rather than returning YGUndefined, change the api to + // return YGFloatOptional. + return YGUndefined; + } + + return static_cast(border).value; +} + +// Yoga specific properties, not compatible with flexbox specification + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT float YGNodeStyleGetAspectRatio(const YGNodeConstRef node) { + const YGFloatOptional op = node->getStyle().aspectRatio(); + return op.isUndefined() ? YGUndefined : op.unwrap(); +} + +// TODO(T26792433): Change the API to accept YGFloatOptional. +YOGA_EXPORT void YGNodeStyleSetAspectRatio( + const YGNodeRef node, + const float aspectRatio) { + updateStyle( + node, &YGStyle::aspectRatio, YGFloatOptional{aspectRatio}); +} + +YOGA_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float points) { + auto value = detail::CompactValue::ofMaybe(points); + updateIndexedStyleProp( + node, &YGStyle::dimensions, YGDimensionWidth, value); +} +YOGA_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float percent) { + auto value = detail::CompactValue::ofMaybe(percent); + updateIndexedStyleProp( + node, &YGStyle::dimensions, YGDimensionWidth, value); +} +YOGA_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node) { + updateIndexedStyleProp( + 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(points); + updateIndexedStyleProp( + node, &YGStyle::dimensions, YGDimensionHeight, value); +} +YOGA_EXPORT void YGNodeStyleSetHeightPercent(YGNodeRef node, float percent) { + auto value = detail::CompactValue::ofMaybe(percent); + updateIndexedStyleProp( + node, &YGStyle::dimensions, YGDimensionHeight, value); +} +YOGA_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node) { + updateIndexedStyleProp( + 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(minWidth); + updateIndexedStyleProp( + node, &YGStyle::minDimensions, YGDimensionWidth, value); +} +YOGA_EXPORT void YGNodeStyleSetMinWidthPercent( + const YGNodeRef node, + const float minWidth) { + auto value = detail::CompactValue::ofMaybe(minWidth); + updateIndexedStyleProp( + 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(minHeight); + updateIndexedStyleProp( + node, &YGStyle::minDimensions, YGDimensionHeight, value); +} +YOGA_EXPORT void YGNodeStyleSetMinHeightPercent( + const YGNodeRef node, + const float minHeight) { + auto value = detail::CompactValue::ofMaybe(minHeight); + updateIndexedStyleProp( + 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(maxWidth); + updateIndexedStyleProp( + node, &YGStyle::maxDimensions, YGDimensionWidth, value); +} +YOGA_EXPORT void YGNodeStyleSetMaxWidthPercent( + const YGNodeRef node, + const float maxWidth) { + auto value = detail::CompactValue::ofMaybe(maxWidth); + updateIndexedStyleProp( + 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(maxHeight); + updateIndexedStyleProp( + node, &YGStyle::maxDimensions, YGDimensionHeight, value); +} +YOGA_EXPORT void YGNodeStyleSetMaxHeightPercent( + const YGNodeRef node, + const float maxHeight) { + auto value = detail::CompactValue::ofMaybe(maxHeight); + updateIndexedStyleProp( + 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); + +YOGA_EXPORT bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout( + const YGNodeRef node) { + return node->getLayout().doesLegacyStretchFlagAffectsLayout(); +} + +std::atomic 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 leading = { + {YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}}; + +const std::array trailing = { + {YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}}; +static const std::array pos = {{ + YGEdgeTop, + YGEdgeBottom, + YGEdgeLeft, + YGEdgeRight, +}}; + +static const std::array dim = { + {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}}; + +static inline float YGNodePaddingAndBorderForAxis( + const 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(node); + + const float baseline = node->baseline( + node->getLayout().measuredDimensions[YGDimensionWidth], + node->getLayout().measuredDimensions[YGDimensionHeight], + layoutContext); + + Event::publish(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() == YGPositionTypeRelative && + child->getStyle().alignSelf() == YGAlignBaseline) { + return true; + } + } + + return false; +} + +static inline float YGNodeDimWithMargin( + const YGNodeRef node, + const YGFlexDirection axis, + const float widthSize) { + return node->getLayout().measuredDimensions[dim[axis]] + + (node->getLeadingMargin(axis, widthSize) + + node->getTrailingMargin(axis, widthSize)) + .unwrap(); +} + +static inline bool YGNodeIsStyleDimDefined( + const YGNodeRef node, + const YGFlexDirection axis, + const float ownerSize) { + bool isUndefined = + YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value); + return !( + node->getResolvedDimension(dim[axis]).unit == YGUnitAuto || + node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint && + !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) || + (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent && + !isUndefined && + (node->getResolvedDimension(dim[axis]).value < 0.0f || + YGFloatIsUndefined(ownerSize)))); +} + +static inline bool YGNodeIsLayoutDimDefined( + const YGNodeRef node, + const YGFlexDirection axis) { + const float value = node->getLayout().measuredDimensions[dim[axis]]; + return !YGFloatIsUndefined(value) && value >= 0.0f; +} + +static YGFloatOptional YGNodeBoundAxisWithinMinAndMax( + const 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; +} + +// Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't +// go below the padding and border amount. +static inline float YGNodeBoundAxis( + const YGNodeRef node, + const YGFlexDirection axis, + const float value, + const float axisSize, + const float widthSize) { + return YGFloatMax( + YGNodeBoundAxisWithinMinAndMax( + node, axis, YGFloatOptional{value}, axisSize) + .unwrap(), + YGNodePaddingAndBorderForAxis(node, axis, widthSize)); +} + +static void YGNodeSetChildTrailingPosition( + const YGNodeRef node, + const YGNodeRef child, + const YGFlexDirection axis) { + const float size = child->getLayout().measuredDimensions[dim[axis]]; + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[axis]] - size - + child->getLayout().position[pos[axis]], + trailing[axis]); +} + +static void YGConstrainMaxSizeForMode( + const 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() || + (YGConfigIsExperimentalFeatureEnabled( + child->getConfig(), YGExperimentalFeatureWebFlexBasis) && + child->getLayout().computedFlexBasisGeneration != generationCount)) { + const YGFloatOptional paddingAndBorder = YGFloatOptional( + YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth)); + child->setLayoutComputedFlexBasis( + YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder)); + } + } else if (isMainAxisRow && isRowStyleDimDefined) { + // The width is definite, so use that as the flex basis. + const YGFloatOptional paddingAndBorder = YGFloatOptional( + YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth)); + + child->setLayoutComputedFlexBasis(YGFloatOptionalMax( + YGResolveValue( + child->getResolvedDimensions()[YGDimensionWidth], ownerWidth), + paddingAndBorder)); + } else if (!isMainAxisRow && isColumnStyleDimDefined) { + // The height is definite, so use that as the flex basis. + const YGFloatOptional paddingAndBorder = + YGFloatOptional(YGNodePaddingAndBorderForAxis( + child, YGFlexDirectionColumn, ownerWidth)); + child->setLayoutComputedFlexBasis(YGFloatOptionalMax( + YGResolveValue( + child->getResolvedDimensions()[YGDimensionHeight], ownerHeight), + paddingAndBorder)); + } else { + // Compute the flex basis and hypothetical main size (i.e. the clamped flex + // basis). + childWidth = YGUndefined; + childHeight = YGUndefined; + childWidthMeasureMode = YGMeasureModeUndefined; + childHeightMeasureMode = YGMeasureModeUndefined; + + auto marginRow = + child->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + auto marginColumn = + child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + if (isRowStyleDimDefined) { + childWidth = + YGResolveValue( + child->getResolvedDimensions()[YGDimensionWidth], ownerWidth) + .unwrap() + + marginRow; + childWidthMeasureMode = YGMeasureModeExactly; + } + if (isColumnStyleDimDefined) { + childHeight = + YGResolveValue( + child->getResolvedDimensions()[YGDimensionHeight], ownerHeight) + .unwrap() + + marginColumn; + childHeightMeasureMode = YGMeasureModeExactly; + } + + // The W3C spec doesn't say anything about the 'overflow' property, but all + // major browsers appear to implement the following logic. + if ((!isMainAxisRow && node->getStyle().overflow() == YGOverflowScroll) || + node->getStyle().overflow() != YGOverflowScroll) { + if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + } + + if ((isMainAxisRow && node->getStyle().overflow() == YGOverflowScroll) || + node->getStyle().overflow() != YGOverflowScroll) { + if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) { + childHeight = height; + childHeightMeasureMode = YGMeasureModeAtMost; + } + } + + 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; + } + } + + // If child has no defined size in the cross axis and is set to stretch, set + // the cross axis to be measured exactly with the available inner width + + const bool hasExactWidth = + !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly; + const bool childWidthStretch = + YGNodeAlignItem(node, child) == YGAlignStretch && + childWidthMeasureMode != YGMeasureModeExactly; + if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && + childWidthStretch) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeExactly; + if (!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); + + // Measure the child + 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 the child doesn't have a specified width, compute the width based on + // the left/right offsets if they're defined. + if (child->isLeadingPositionDefined(YGFlexDirectionRow) && + child->isTrailingPosDefined(YGFlexDirectionRow)) { + childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] - + (node->getLeadingBorder(YGFlexDirectionRow) + + node->getTrailingBorder(YGFlexDirectionRow)) - + (child->getLeadingPosition(YGFlexDirectionRow, width) + + child->getTrailingPosition(YGFlexDirectionRow, width)) + .unwrap(); + childWidth = + YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width); + } + } + + if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) { + childHeight = YGResolveValue( + child->getResolvedDimensions()[YGDimensionHeight], height) + .unwrap() + + marginColumn; + } else { + // If the child doesn't have a specified height, compute the height based on + // the top/bottom offsets if they're defined. + if (child->isLeadingPositionDefined(YGFlexDirectionColumn) && + child->isTrailingPosDefined(YGFlexDirectionColumn)) { + childHeight = node->getLayout().measuredDimensions[YGDimensionHeight] - + (node->getLeadingBorder(YGFlexDirectionColumn) + + node->getTrailingBorder(YGFlexDirectionColumn)) - + (child->getLeadingPosition(YGFlexDirectionColumn, height) + + child->getTrailingPosition(YGFlexDirectionColumn, height)) + .unwrap(); + childHeight = YGNodeBoundAxis( + child, YGFlexDirectionColumn, childHeight, height, width); + } + } + + // Exactly one dimension needs to be defined for us to be able to do aspect + // ratio calculation. One dimension being the anchor and the other being + // flexible. + 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 we're still missing one or the other dimension, measure the content. + if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) { + childWidthMeasureMode = YGFloatIsUndefined(childWidth) + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + childHeightMeasureMode = YGFloatIsUndefined(childHeight) + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + + // If the size of the owner is defined then try to constrain the absolute + // child to that size as well. This allows text within the absolute child to + // wrap to the size of its owner. This is the same behavior as many browsers + // implement. + if (!isMainAxisRow && YGFloatIsUndefined(childWidth) && + widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) && + width > 0) { + childWidth = width; + childWidthMeasureMode = YGMeasureModeAtMost; + } + + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + childWidthMeasureMode, + childHeightMeasureMode, + childWidth, + childHeight, + false, + 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, width).unwrap() - + child->getTrailingPosition(mainAxis, isMainAxisRow ? width : height) + .unwrap(), + leading[mainAxis]); + } else if ( + !child->isLeadingPositionDefined(mainAxis) && + node->getStyle().justifyContent() == YGJustifyCenter) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[mainAxis]] - + child->getLayout().measuredDimensions[dim[mainAxis]]) / + 2.0f, + leading[mainAxis]); + } else if ( + !child->isLeadingPositionDefined(mainAxis) && + node->getStyle().justifyContent() == YGJustifyFlexEnd) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[mainAxis]] - + child->getLayout().measuredDimensions[dim[mainAxis]]), + leading[mainAxis]); + } + + if (child->isTrailingPosDefined(crossAxis) && + !child->isLeadingPositionDefined(crossAxis)) { + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]] - + node->getTrailingBorder(crossAxis) - + child->getTrailingMargin(crossAxis, width).unwrap() - + child + ->getTrailingPosition(crossAxis, isMainAxisRow ? height : width) + .unwrap(), + leading[crossAxis]); + + } else if ( + !child->isLeadingPositionDefined(crossAxis) && + YGNodeAlignItem(node, child) == YGAlignCenter) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]]) / + 2.0f, + leading[crossAxis]); + } else if ( + !child->isLeadingPositionDefined(crossAxis) && + ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^ + (node->getStyle().flexWrap() == YGWrapWrapReverse))) { + child->setLayoutPosition( + (node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]]), + leading[crossAxis]); + } +} + +static void YGNodeWithMeasureFuncSetMeasuredDimensions( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + LayoutData& layoutMarkerData, + void* const layoutContext, + const LayoutPassReason reason) { + YGAssertWithNode( + node, + node->hasMeasureFunc(), + "Expected node to have custom measure function"); + + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth); + const float paddingAndBorderAxisColumn = YGNodePaddingAndBorderForAxis( + node, YGFlexDirectionColumn, availableWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, availableWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, availableWidth).unwrap(); + + // We want to make sure we don't call measure with negative size + const float innerWidth = YGFloatIsUndefined(availableWidth) + ? availableWidth + : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + const float innerHeight = YGFloatIsUndefined(availableHeight) + ? availableHeight + : YGFloatMax( + 0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); + + if (widthMeasureMode == YGMeasureModeExactly && + heightMeasureMode == YGMeasureModeExactly) { + // Don't bother sizing the text if both dimensions are already defined. + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + } else { + Event::publish(node); + + // Measure the text under the current constraints. + const YGSize measuredSize = node->measure( + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode, + layoutContext); + + layoutMarkerData.measureCallbacks += 1; + layoutMarkerData.measureCallbackReasonsCount[static_cast(reason)] += + 1; + + Event::publish( + node, + {layoutContext, + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode, + measuredSize.width, + measuredSize.height, + reason}); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? measuredSize.width + paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? measuredSize.height + paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + } +} + +// For nodes with no children, use the available values if they were provided, +// or the minimum size as indicated by the padding and border sizes. +static void YGNodeEmptyContainerSetMeasuredDimensions( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight) { + const float paddingAndBorderAxisRow = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth); + const float paddingAndBorderAxisColumn = + YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth); + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + (widthMeasureMode == YGMeasureModeUndefined || + widthMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisRow + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + (heightMeasureMode == YGMeasureModeUndefined || + heightMeasureMode == YGMeasureModeAtMost) + ? paddingAndBorderAxisColumn + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); +} + +static bool YGNodeFixedSizeSetMeasuredDimensions( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight) { + if ((!YGFloatIsUndefined(availableWidth) && + widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) || + (!YGFloatIsUndefined(availableHeight) && + heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) || + (widthMeasureMode == YGMeasureModeExactly && + heightMeasureMode == YGMeasureModeExactly)) { + auto marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + auto marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + YGFloatIsUndefined(availableWidth) || + (widthMeasureMode == YGMeasureModeAtMost && + availableWidth < 0.0f) + ? 0.0f + : availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + YGFloatIsUndefined(availableHeight) || + (heightMeasureMode == YGMeasureModeAtMost && + availableHeight < 0.0f) + ? 0.0f + : availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + return true; + } + + return false; +} + +static void YGZeroOutLayoutRecursivly( + const YGNodeRef node, + void* layoutContext) { + node->getLayout() = {}; + node->setLayoutDimension(0, 0); + node->setLayoutDimension(0, 1); + node->setHasNewLayout(true); + + node->iterChildrenAfterCloningIfNeeded( + YGZeroOutLayoutRecursivly, layoutContext); +} + +static float YGNodeCalculateAvailableInnerDim( + const YGNodeConstRef node, + YGFlexDirection axis, + float availableDim, + float ownerDim) { + YGFlexDirection direction = + YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn; + YGDimension dimension = + YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight; + + const float margin = node->getMarginForAxis(direction, ownerDim).unwrap(); + const float paddingAndBorder = + YGNodePaddingAndBorderForAxis(node, direction, ownerDim); + + float availableInnerDim = availableDim - margin - paddingAndBorder; + // Max dimension overrides predefined dimension value; Min dimension in turn + // overrides both of the above + if (!YGFloatIsUndefined(availableInnerDim)) { + // We want to make sure our available height does not violate min and max + // constraints + const YGFloatOptional minDimensionOptional = + YGResolveValue(node->getStyle().minDimensions()[dimension], ownerDim); + const float minInnerDim = minDimensionOptional.isUndefined() + ? 0.0f + : minDimensionOptional.unwrap() - paddingAndBorder; + + const YGFloatOptional maxDimensionOptional = + YGResolveValue(node->getStyle().maxDimensions()[dimension], ownerDim); + + const float maxInnerDim = maxDimensionOptional.isUndefined() + ? FLT_MAX + : maxDimensionOptional.unwrap() - paddingAndBorder; + availableInnerDim = + YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim); + } + + return availableInnerDim; +} + +static float YGNodeComputeFlexBasisForChildren( + const YGNodeRef node, + const float availableInnerWidth, + const float availableInnerHeight, + YGMeasureMode widthMeasureMode, + YGMeasureMode heightMeasureMode, + YGDirection direction, + YGFlexDirection mainAxis, + const YGConfigRef config, + bool performLayout, + 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 there is only one child with flexGrow + flexShrink it means we can set + // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the + // child to exactly match the remaining space + if (measureModeMainDim == YGMeasureModeExactly) { + for (auto child : children) { + if (child->isNodeFlexible()) { + if (singleFlexChild != nullptr || + YGFloatsEqual(child->resolveFlexGrow(), 0.0f) || + YGFloatsEqual(child->resolveFlexShrink(), 0.0f)) { + // There is already a flexible child, or this flexible child doesn't + // have flexGrow and flexShrink, abort + singleFlexChild = nullptr; + break; + } else { + singleFlexChild = child; + } + } + } + } + + for (auto child : children) { + child->resolveDimension(); + if (child->getStyle().display() == YGDisplayNone) { + YGZeroOutLayoutRecursivly(child, layoutContext); + child->setHasNewLayout(true); + child->setDirty(false); + continue; + } + if (performLayout) { + // Set the initial position (relative to the owner). + const YGDirection childDirection = child->resolveDirection(direction); + const float mainDim = YGFlexDirectionIsRow(mainAxis) + ? availableInnerWidth + : availableInnerHeight; + const float crossDim = YGFlexDirectionIsRow(mainAxis) + ? availableInnerHeight + : availableInnerWidth; + child->setPosition( + childDirection, mainDim, crossDim, availableInnerWidth); + } + + if (child->getStyle().positionType() == YGPositionTypeAbsolute) { + continue; + } + if (child == singleFlexChild) { + child->setLayoutComputedFlexBasisGeneration(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; +} + +// This function assumes that all the children of node have their +// computedFlexBasis properly computed(To do this use +// YGNodeComputeFlexBasisForChildren function). This function calculates +// YGCollectFlexItemsRowMeasurement +static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues( + const YGNodeRef& node, + const YGDirection ownerDirection, + const float mainAxisownerSize, + const float availableInnerWidth, + const float availableInnerMainDim, + const uint32_t startOfLineIndex, + const uint32_t lineCount) { + YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {}; + flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size()); + + float sizeConsumedOnCurrentLineIncludingMinConstraint = 0; + const YGFlexDirection mainAxis = YGResolveFlexDirection( + node->getStyle().flexDirection(), node->resolveDirection(ownerDirection)); + const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; + + // Add items to the current line until it's full or we run out of items. + uint32_t endOfLineIndex = startOfLineIndex; + for (; endOfLineIndex < node->getChildren().size(); endOfLineIndex++) { + const YGNodeRef child = node->getChild(endOfLineIndex); + if (child->getStyle().display() == YGDisplayNone || + child->getStyle().positionType() == YGPositionTypeAbsolute) { + continue; + } + child->setLineIndex(lineCount); + const float childMarginMainAxis = + child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap(); + const float flexBasisWithMinAndMaxConstraints = + YGNodeBoundAxisWithinMinAndMax( + child, + mainAxis, + child->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + + // If this is a multi-line flow and this item pushes us over the available + // size, we've hit the end of the current line. Break out of the loop and + // lay out the current line. + if (sizeConsumedOnCurrentLineIncludingMinConstraint + + flexBasisWithMinAndMaxConstraints + childMarginMainAxis > + availableInnerMainDim && + isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) { + break; + } + + sizeConsumedOnCurrentLineIncludingMinConstraint += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + flexAlgoRowMeasurement.sizeConsumedOnCurrentLine += + flexBasisWithMinAndMaxConstraints + childMarginMainAxis; + flexAlgoRowMeasurement.itemsOnLine++; + + if (child->isNodeFlexible()) { + flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow(); + + // Unlike the grow factor, the shrink factor is scaled relative to the + // child dimension. + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors += + -child->resolveFlexShrink() * + child->getLayout().computedFlexBasis.unwrap(); + } + + flexAlgoRowMeasurement.relativeChildren.push_back(child); + } + + // The total flex factor needs to be floored to 1. + if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 && + flexAlgoRowMeasurement.totalFlexGrowFactors < 1) { + flexAlgoRowMeasurement.totalFlexGrowFactors = 1; + } + + // The total flex shrink factor needs to be floored to 1. + if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 && + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) { + flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1; + } + flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex; + return flexAlgoRowMeasurement; +} + +// It distributes the free space to the flexible items and ensures that the size +// of the flex items abide the min and max constraints. At the end of this +// function the child nodes would have proper size. Prior using this function +// please ensure that YGDistributeFreeSpaceFirstPass is called. +static float YGDistributeFreeSpaceSecondPass( + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGNodeRef node, + const YGFlexDirection mainAxis, + const YGFlexDirection crossAxis, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config, + 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; + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + float childSize; + + if (!YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexShrinkScaledFactors) && + collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) { + childSize = childFlexBasis + flexShrinkScaledFactor; + } else { + childSize = childFlexBasis + + (collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors) * + flexShrinkScaledFactor; + } + + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childSize, + availableInnerMainDim, + availableInnerWidth); + } + } else if ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { + updatedMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * + flexGrowFactor, + availableInnerMainDim, + availableInnerWidth); + } + } + + deltaFreeSpace += updatedMainSize - childFlexBasis; + + const float marginMain = + currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth) + .unwrap(); + const float marginCross = + currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap(); + + float childCrossSize; + float childMainSize = updatedMainSize + marginMain; + YGMeasureMode childCrossMeasureMode; + YGMeasureMode childMainMeasureMode = YGMeasureModeExactly; + + 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 && flexBasisOverflows) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + currentRelativeChild->marginLeadingValue(crossAxis).unit != + YGUnitAuto && + currentRelativeChild->marginTrailingValue(crossAxis).unit != + YGUnitAuto) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = YGMeasureModeExactly; + } else if (!YGNodeIsStyleDimDefined( + currentRelativeChild, crossAxis, availableInnerCrossDim)) { + childCrossSize = availableInnerCrossDim; + childCrossMeasureMode = YGFloatIsUndefined(childCrossSize) + ? YGMeasureModeUndefined + : YGMeasureModeAtMost; + } else { + childCrossSize = + YGResolveValue( + currentRelativeChild->getResolvedDimension(dim[crossAxis]), + availableInnerCrossDim) + .unwrap() + + marginCross; + const bool isLoosePercentageMeasurement = + currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit == + YGUnitPercent && + measureModeCrossDim != YGMeasureModeExactly; + childCrossMeasureMode = + YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement + ? YGMeasureModeUndefined + : YGMeasureModeExactly; + } + + YGConstrainMaxSizeForMode( + currentRelativeChild, + mainAxis, + availableInnerMainDim, + availableInnerWidth, + &childMainMeasureMode, + &childMainSize); + YGConstrainMaxSizeForMode( + currentRelativeChild, + crossAxis, + availableInnerCrossDim, + availableInnerWidth, + &childCrossMeasureMode, + &childCrossSize); + + const bool requiresStretchLayout = + !YGNodeIsStyleDimDefined( + currentRelativeChild, crossAxis, availableInnerCrossDim) && + YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch && + currentRelativeChild->marginLeadingValue(crossAxis).unit != + YGUnitAuto && + currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto; + + const float childWidth = isMainAxisRow ? childMainSize : childCrossSize; + const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize; + + const YGMeasureMode childWidthMeasureMode = + isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + const YGMeasureMode childHeightMeasureMode = + !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode; + + const bool isLayoutPass = performLayout && !requiresStretchLayout; + // Recursively call the layout algorithm for this child with the updated + // main size. + 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; +} + +// It distributes the free space to the flexible items.For those flexible items +// whose min and max constraints are triggered, those flex item's clamped size +// is removed from the remaingfreespace. +static void YGDistributeFreeSpaceFirstPass( + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGFlexDirection mainAxis, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerWidth) { + float flexShrinkScaledFactor = 0; + float flexGrowFactor = 0; + float baseMainSize = 0; + float boundMainSize = 0; + float deltaFreeSpace = 0; + + for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) { + float childFlexBasis = + YGNodeBoundAxisWithinMinAndMax( + currentRelativeChild, + mainAxis, + currentRelativeChild->getLayout().computedFlexBasis, + mainAxisownerSize) + .unwrap(); + + if (collectedFlexItemsValues.remainingFreeSpace < 0) { + flexShrinkScaledFactor = + -currentRelativeChild->resolveFlexShrink() * childFlexBasis; + + // Is this child able to shrink? + if (!YGFloatIsUndefined(flexShrinkScaledFactor) && + flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexShrinkScaledFactors * + flexShrinkScaledFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this + // item's min/max constraints should also trigger in the second pass + // resulting in the item's size calculation being identical in the + // first and second passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + collectedFlexItemsValues.totalFlexShrinkScaledFactors -= + flexShrinkScaledFactor; + } + } + } else if ( + !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) && + collectedFlexItemsValues.remainingFreeSpace > 0) { + flexGrowFactor = currentRelativeChild->resolveFlexGrow(); + + // Is this child able to grow? + if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor; + boundMainSize = YGNodeBoundAxis( + currentRelativeChild, + mainAxis, + baseMainSize, + availableInnerMainDim, + availableInnerWidth); + + if (!YGFloatIsUndefined(baseMainSize) && + !YGFloatIsUndefined(boundMainSize) && + baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this + // item's min/max constraints should also trigger in the second pass + // resulting in the item's size calculation being identical in the + // first and second passes. + deltaFreeSpace += boundMainSize - childFlexBasis; + collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor; + } + } + } + } + collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace; +} + +// Do two passes over the flex items to figure out how to distribute the +// remaining space. +// +// The first pass finds the items whose min/max constraints trigger, freezes +// them at those sizes, and excludes those sizes from the remaining space. +// +// The second pass sets the size of each flexible item. It distributes the +// remaining space amongst the items whose min/max constraints didn't trigger in +// the first pass. For the other items, it sets their sizes by forcing their +// min/max constraints to trigger again. +// +// This two pass approach for resolving min/max constraints deviates from the +// spec. The spec +// (https://www.w3.org/TR/CSS-flexbox-1/#resolve-flexible-lengths) describes a +// process that needs to be repeated a variable number of times. The algorithm +// implemented here won't handle all cases but it was simpler to implement and +// it mitigates performance concerns because we know exactly how many passes +// it'll do. +// +// At the end of this function the child nodes would have the proper size +// assigned to them. +// +static void YGResolveFlexibleLength( + const YGNodeRef node, + YGCollectFlexItemsRowValues& collectedFlexItemsValues, + const YGFlexDirection mainAxis, + const YGFlexDirection crossAxis, + const float mainAxisownerSize, + const float availableInnerMainDim, + const float availableInnerCrossDim, + const float availableInnerWidth, + const float availableInnerHeight, + const bool flexBasisOverflows, + const YGMeasureMode measureModeCrossDim, + const bool performLayout, + const YGConfigRef config, + LayoutData& layoutMarkerData, + void* const layoutContext, + const uint32_t depth, + const uint32_t generationCount) { + const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace; + // First pass: detect the flex items whose min/max constraints trigger + YGDistributeFreeSpaceFirstPass( + collectedFlexItemsValues, + mainAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerWidth); + + // Second pass: resolve the sizes of the flexible items + const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass( + collectedFlexItemsValues, + node, + mainAxis, + crossAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config, + layoutMarkerData, + layoutContext, + 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(); + // If we are using "at most" rules in the main axis, make sure that + // remainingFreeSpace is 0 when min main dimension is not given + if (measureModeMainDim == YGMeasureModeAtMost && + collectedFlexItemsValues.remainingFreeSpace > 0) { + if (!style.minDimensions()[dim[mainAxis]].isUndefined() && + !YGResolveValue(style.minDimensions()[dim[mainAxis]], mainAxisownerSize) + .isUndefined()) { + // This condition makes sure that if the size of main dimension(after + // considering child nodes main dim, leading and trailing padding etc) + // falls below min dimension, then the remainingFreeSpace is reassigned + // considering the min dimension + + // `minAvailableMainDim` denotes minimum available space in which child + // can be laid out, it will exclude space consumed by padding and border. + const float minAvailableMainDim = + YGResolveValue( + style.minDimensions()[dim[mainAxis]], mainAxisownerSize) + .unwrap() - + leadingPaddingAndBorderMain - trailingPaddingAndBorderMain; + const float occupiedSpaceByChildNodes = + availableInnerMainDim - collectedFlexItemsValues.remainingFreeSpace; + collectedFlexItemsValues.remainingFreeSpace = + YGFloatMax(0, minAvailableMainDim - occupiedSpaceByChildNodes); + } else { + collectedFlexItemsValues.remainingFreeSpace = 0; + } + } + + int numberOfAutoMarginsOnCurrentLine = 0; + for (uint32_t i = startOfLineIndex; + i < collectedFlexItemsValues.endOfLineIndex; + i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().positionType() == YGPositionTypeRelative) { + if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) { + numberOfAutoMarginsOnCurrentLine++; + } + } + } + + // In order to position the elements in the main axis, we have two controls. + // The space between the beginning and the first element and the space between + // each two elements. + float leadingMainDim = 0; + float betweenMainDim = 0; + const YGJustify justifyContent = node->getStyle().justifyContent(); + + if (numberOfAutoMarginsOnCurrentLine == 0) { + switch (justifyContent) { + case YGJustifyCenter: + leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2; + break; + case YGJustifyFlexEnd: + leadingMainDim = collectedFlexItemsValues.remainingFreeSpace; + break; + case YGJustifySpaceBetween: + if (collectedFlexItemsValues.itemsOnLine > 1) { + betweenMainDim = + YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) / + (collectedFlexItemsValues.itemsOnLine - 1); + } else { + betweenMainDim = 0; + } + break; + case YGJustifySpaceEvenly: + // Space is distributed evenly across all elements + betweenMainDim = collectedFlexItemsValues.remainingFreeSpace / + (collectedFlexItemsValues.itemsOnLine + 1); + leadingMainDim = betweenMainDim; + break; + case YGJustifySpaceAround: + // Space on the edges is half of the space between elements + betweenMainDim = collectedFlexItemsValues.remainingFreeSpace / + collectedFlexItemsValues.itemsOnLine; + leadingMainDim = betweenMainDim / 2; + break; + case YGJustifyFlexStart: + break; + } + } + + collectedFlexItemsValues.mainDim = + leadingPaddingAndBorderMain + leadingMainDim; + collectedFlexItemsValues.crossDim = 0; + + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + bool isNodeBaselineLayout = YGIsBaselineLayout(node); + for (uint32_t i = startOfLineIndex; + i < collectedFlexItemsValues.endOfLineIndex; + i++) { + const YGNodeRef child = node->getChild(i); + const YGStyle& childStyle = child->getStyle(); + const YGLayout childLayout = child->getLayout(); + if (childStyle.display() == YGDisplayNone) { + continue; + } + if (childStyle.positionType() == YGPositionTypeAbsolute && + child->isLeadingPositionDefined(mainAxis)) { + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said (and + // margin/border). + child->setLayoutPosition( + child->getLeadingPosition(mainAxis, availableInnerMainDim) + .unwrap() + + node->getLeadingBorder(mainAxis) + + child->getLeadingMargin(mainAxis, availableInnerWidth).unwrap(), + pos[mainAxis]); + } + } else { + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements do not + // take part in that phase. + if (childStyle.positionType() == YGPositionTypeRelative) { + if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) { + collectedFlexItemsValues.mainDim += + collectedFlexItemsValues.remainingFreeSpace / + numberOfAutoMarginsOnCurrentLine; + } + + if (performLayout) { + child->setLayoutPosition( + childLayout.position[pos[mainAxis]] + + collectedFlexItemsValues.mainDim, + pos[mainAxis]); + } + + if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) { + collectedFlexItemsValues.mainDim += + collectedFlexItemsValues.remainingFreeSpace / + numberOfAutoMarginsOnCurrentLine; + } + bool canSkipFlex = + !performLayout && measureModeCrossDim == YGMeasureModeExactly; + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims + // because they weren't computed. This means we can't call + // YGNodeDimWithMargin. + collectedFlexItemsValues.mainDim += betweenMainDim + + child->getMarginForAxis(mainAxis, availableInnerWidth).unwrap() + + childLayout.computedFlexBasis.unwrap(); + collectedFlexItemsValues.crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + collectedFlexItemsValues.mainDim += betweenMainDim + + YGNodeDimWithMargin(child, mainAxis, availableInnerWidth); + + if (isNodeBaselineLayout) { + // If the child is baseline aligned then the cross dimension is + // calculated by adding maxAscent and maxDescent from the baseline. + const float ascent = YGBaseline(child, layoutContext) + + child + ->getLeadingMargin( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap(); + const float descent = + child->getLayout().measuredDimensions[YGDimensionHeight] + + child + ->getMarginForAxis( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap() - + ascent; + + maxAscentForCurrentLine = + YGFloatMax(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + YGFloatMax(maxDescentForCurrentLine, descent); + } else { + // The cross dimension is the max of the elements dimension since + // there can only be one element in that cross dimension in the case + // when the items are not baseline aligned + collectedFlexItemsValues.crossDim = YGFloatMax( + collectedFlexItemsValues.crossDim, + YGNodeDimWithMargin(child, crossAxis, availableInnerWidth)); + } + } + } else if (performLayout) { + child->setLayoutPosition( + childLayout.position[pos[mainAxis]] + + node->getLeadingBorder(mainAxis) + leadingMainDim, + pos[mainAxis]); + } + } + } + collectedFlexItemsValues.mainDim += trailingPaddingAndBorderMain; + + if (isNodeBaselineLayout) { + collectedFlexItemsValues.crossDim = + maxAscentForCurrentLine + maxDescentForCurrentLine; + } +} + +// +// This is the main routine that implements a subset of the flexbox layout +// algorithm described in the W3C CSS documentation: +// https://www.w3.org/TR/CSS3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, +// which are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes +// are stacked in document order. +// * The 'order' property is not supported. The order of flex items is always +// defined by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of +// 'collapse' and 'hidden' are not supported. +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or +// bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default +// minimum main size. For text blocks, for example, this is the width of the +// widest word. Calculating the minimum width is expensive, so we forego it +// and assume a default minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible +// lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', +// but the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and layed out +// - availableWidth & availableHeight: available size to be used for sizing +// the node or YGUndefined if the size is not available; interpretation +// depends on layout flags +// - ownerDirection: the inline (text) direction within the owner +// (left-to-right or right-to-left) +// - widthMeasureMode: indicates the sizing rules for the width (see below +// for explanation) +// - heightMeasureMode: indicates the sizing rules for the height (see below +// for explanation) +// - performLayout: specifies whether the caller is interested in just the +// dimensions of the node or it requires the entire node and its subtree to +// be layed out (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox +// elements. It uses the information in node.style, which is treated as a +// read-only input. It is responsible for setting the layout.direction and +// layout.measuredDimensions fields for the input node as well as the +// layout.position and layout.lineIndex fields for its child nodes. The +// layout.measuredDimensions field includes any border or padding for the +// node but does not include margins. +// +// The spec describes four different layout modes: "fill available", "max +// content", "min content", and "fit content". Of these, we don't use "min +// content" because we don't support default minimum main sizes (see above +// for details). Each of our measure modes maps to a layout mode from the +// spec (https://www.w3.org/TR/CSS3-sizing/#terms): +// - YGMeasureModeUndefined: max content +// - YGMeasureModeExactly: fill available +// - YGMeasureModeAtMost: fit content +// +// When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller +// passes an available size of undefined then it must also pass a measure +// mode of YGMeasureModeUndefined in that dimension. +// +static void YGNodelayoutImpl( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection ownerDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const YGConfigRef config, + 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; + + // Set the resolved resolution in the node's layout. + const YGDirection direction = node->resolveDirection(ownerDirection); + node->setLayoutDirection(direction); + + const YGFlexDirection flexRowDirection = + YGResolveFlexDirection(YGFlexDirectionRow, direction); + const YGFlexDirection flexColumnDirection = + YGResolveFlexDirection(YGFlexDirectionColumn, direction); + + const YGEdge startEdge = + direction == YGDirectionLTR ? YGEdgeLeft : YGEdgeRight; + const YGEdge endEdge = direction == YGDirectionLTR ? YGEdgeRight : YGEdgeLeft; + node->setLayoutMargin( + node->getLeadingMargin(flexRowDirection, ownerWidth).unwrap(), startEdge); + node->setLayoutMargin( + node->getTrailingMargin(flexRowDirection, ownerWidth).unwrap(), endEdge); + node->setLayoutMargin( + node->getLeadingMargin(flexColumnDirection, ownerWidth).unwrap(), + YGEdgeTop); + node->setLayoutMargin( + node->getTrailingMargin(flexColumnDirection, ownerWidth).unwrap(), + YGEdgeBottom); + + 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, + availableHeight, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + layoutMarkerData, + layoutContext, + reason); + return; + } + + const uint32_t childCount = YGNodeGetChildCount(node); + if (childCount == 0) { + YGNodeEmptyContainerSetMeasuredDimensions( + node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight); + return; + } + + // If we're not being asked to perform a full layout we can skip the algorithm + // if we already know the size + if (!performLayout && + YGNodeFixedSizeSetMeasuredDimensions( + node, + availableWidth, + availableHeight, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight)) { + return; + } + + // At this point we know we're going to perform work. Ensure that each child + // has a mutable copy. + node->cloneChildrenIfNeeded(layoutContext); + // Reset layout flags, as they could have changed. + node->setLayoutHadOverflow(false); + + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + const YGFlexDirection mainAxis = + YGResolveFlexDirection(node->getStyle().flexDirection(), direction); + const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction); + const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis); + const bool isNodeFlexWrap = node->getStyle().flexWrap() != YGWrapNoWrap; + + const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight; + const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth; + + const float leadingPaddingAndBorderCross = + node->getLeadingPaddingAndBorder(crossAxis, ownerWidth).unwrap(); + const float paddingAndBorderAxisMain = + YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth); + const float paddingAndBorderAxisCross = + YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth); + + YGMeasureMode measureModeMainDim = + isMainAxisRow ? widthMeasureMode : heightMeasureMode; + YGMeasureMode measureModeCrossDim = + isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + const float paddingAndBorderAxisRow = + isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross; + const float paddingAndBorderAxisColumn = + isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain; + + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + const 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; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + + float availableInnerWidth = YGNodeCalculateAvailableInnerDim( + node, YGFlexDirectionRow, availableWidth, ownerWidth); + float availableInnerHeight = YGNodeCalculateAvailableInnerDim( + node, YGFlexDirectionColumn, availableHeight, ownerHeight); + + float availableInnerMainDim = + isMainAxisRow ? availableInnerWidth : availableInnerHeight; + const float availableInnerCrossDim = + isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + + float totalOuterFlexBasis = YGNodeComputeFlexBasisForChildren( + node, + availableInnerWidth, + availableInnerHeight, + widthMeasureMode, + heightMeasureMode, + direction, + mainAxis, + config, + performLayout, + layoutMarkerData, + layoutContext, + depth, + generationCount); + + const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined + ? false + : totalOuterFlexBasis > availableInnerMainDim; + if (isNodeFlexWrap && flexBasisOverflows && + measureModeMainDim == YGMeasureModeAtMost) { + measureModeMainDim = YGMeasureModeExactly; + } + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + uint32_t startOfLineIndex = 0; + uint32_t endOfLineIndex = 0; + + // Number of lines. + uint32_t lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; + + // Max main dimension of all the lines. + float maxLineMainDim = 0; + YGCollectFlexItemsRowValues collectedFlexItemsValues; + for (; endOfLineIndex < childCount; + lineCount++, startOfLineIndex = endOfLineIndex) { + collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues( + node, + ownerDirection, + mainAxisownerSize, + availableInnerWidth, + availableInnerMainDim, + startOfLineIndex, + lineCount); + endOfLineIndex = collectedFlexItemsValues.endOfLineIndex; + + // If we don't need to measure the cross axis, we can skip the entire flex + // step. + const bool canSkipFlex = + !performLayout && measureModeCrossDim == YGMeasureModeExactly; + + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. If + // the main dimension size isn't known, it is computed based on the line + // length, so there's no more space left to distribute. + + bool sizeBasedOnContent = false; + // If we don't measure with exact main dimension we want to ensure we don't + // violate min and max + if (measureModeMainDim != YGMeasureModeExactly) { + if (!YGFloatIsUndefined(minInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine < + minInnerMainDim) { + availableInnerMainDim = minInnerMainDim; + } else if ( + !YGFloatIsUndefined(maxInnerMainDim) && + collectedFlexItemsValues.sizeConsumedOnCurrentLine > + maxInnerMainDim) { + availableInnerMainDim = maxInnerMainDim; + } else { + if (!node->getConfig()->useLegacyStretchBehaviour && + ((YGFloatIsUndefined( + collectedFlexItemsValues.totalFlexGrowFactors) && + collectedFlexItemsValues.totalFlexGrowFactors == 0) || + (YGFloatIsUndefined(node->resolveFlexGrow()) && + node->resolveFlexGrow() == 0))) { + // If we don't have any children to flex or we can't flex the node + // itself, space we've used is all space we need. Root node also + // should be shrunk to minimum + availableInnerMainDim = + collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } + + if (node->getConfig()->useLegacyStretchBehaviour) { + node->setLayoutDidUseLegacyFlag(true); + } + sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour; + } + } + + if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) { + collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim - + collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized + // based on its content. sizeConsumedOnCurrentLine is negative which means + // the node will allocate 0 points for its content. Consequently, + // remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + collectedFlexItemsValues.remainingFreeSpace = + -collectedFlexItemsValues.sizeConsumedOnCurrentLine; + } + + if (!canSkipFlex) { + YGResolveFlexibleLength( + node, + collectedFlexItemsValues, + mainAxis, + crossAxis, + mainAxisownerSize, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + availableInnerHeight, + flexBasisOverflows, + measureModeCrossDim, + performLayout, + config, + layoutMarkerData, + layoutContext, + depth, + generationCount); + } + + node->setLayoutHadOverflow( + node->getLayout().hadOverflow() | + (collectedFlexItemsValues.remainingFreeSpace < 0)); + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main + // axis. Their dimensions are also set in the cross axis with the exception + // of items that are aligned "stretch". We need to compute these stretch + // values and set the final positions. + + YGJustifyMainAxis( + node, + collectedFlexItemsValues, + startOfLineIndex, + mainAxis, + crossAxis, + measureModeMainDim, + measureModeCrossDim, + mainAxisownerSize, + ownerWidth, + availableInnerMainDim, + availableInnerCrossDim, + availableInnerWidth, + performLayout, + layoutContext); + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == YGMeasureModeUndefined || + measureModeCrossDim == YGMeasureModeAtMost) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = + YGNodeBoundAxis( + node, + crossAxis, + collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth) - + paddingAndBorderAxisCross; + } + + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) { + collectedFlexItemsValues.crossDim = availableInnerCrossDim; + } + + // Clamp to the min/max size specified on the container. + collectedFlexItemsValues.crossDim = + YGNodeBoundAxis( + node, + crossAxis, + collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth) - + paddingAndBorderAxisCross; + + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().display() == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType() == YGPositionTypeAbsolute) { + // If the child is absolutely positioned and has a + // top/left/bottom/right set, override all the previously computed + // positions to set it correctly. + const bool isChildLeadingPosDefined = + child->isLeadingPositionDefined(crossAxis); + if (isChildLeadingPosDefined) { + child->setLayoutPosition( + child->getLeadingPosition(crossAxis, availableInnerCrossDim) + .unwrap() + + node->getLeadingBorder(crossAxis) + + child->getLeadingMargin(crossAxis, availableInnerWidth) + .unwrap(), + pos[crossAxis]); + } + // If leading position is not defined or calculations result in Nan, + // default to border + margin + if (!isChildLeadingPosDefined || + YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) { + child->setLayoutPosition( + node->getLeadingBorder(crossAxis) + + child->getLeadingMargin(crossAxis, availableInnerWidth) + .unwrap(), + pos[crossAxis]); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (owner) or + // alignSelf (child) in order to determine the position in the cross + // axis + const YGAlign alignItem = YGNodeAlignItem(node, child); + + // If the child uses align stretch, we need to lay it out one more + // time, this time forcing the cross-axis size to be the computed + // cross size for the current line. + if (alignItem == YGAlignStretch && + child->marginLeadingValue(crossAxis).unit != YGUnitAuto && + child->marginTrailingValue(crossAxis).unit != YGUnitAuto) { + // If the child defines a definite size for its cross axis, there's + // no need to stretch. + if (!YGNodeIsStyleDimDefined( + child, crossAxis, availableInnerCrossDim)) { + float childMainSize = + child->getLayout().measuredDimensions[dim[mainAxis]]; + 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) { + // No-Op + } else if ( + child->marginLeadingValue(crossAxis).unit == YGUnitAuto) { + leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim); + } else if (alignItem == YGAlignFlexStart) { + // No-Op + } else if (alignItem == YGAlignCenter) { + leadingCrossDim += remainingCrossDim / 2; + } else { + leadingCrossDim += remainingCrossDim; + } + } + // And we apply the position + child->setLayoutPosition( + child->getLayout().position[pos[crossAxis]] + totalLineCrossDim + + leadingCrossDim, + pos[crossAxis]); + } + } + } + + totalLineCrossDim += collectedFlexItemsValues.crossDim; + maxLineMainDim = + YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim); + } + + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + // currentLead stores the size of the cross dim + if (performLayout && (isNodeFlexWrap || YGIsBaselineLayout(node))) { + float crossDimLead = 0; + float currentLead = leadingPaddingAndBorderCross; + if (!YGFloatIsUndefined(availableInnerCrossDim)) { + const float remainingAlignContentDim = + availableInnerCrossDim - totalLineCrossDim; + switch (node->getStyle().alignContent()) { + case YGAlignFlexEnd: + currentLead += remainingAlignContentDim; + break; + case YGAlignCenter: + currentLead += remainingAlignContentDim / 2; + break; + case YGAlignStretch: + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = remainingAlignContentDim / lineCount; + } + break; + case YGAlignSpaceAround: + if (availableInnerCrossDim > totalLineCrossDim) { + currentLead += remainingAlignContentDim / (2 * lineCount); + if (lineCount > 1) { + crossDimLead = remainingAlignContentDim / lineCount; + } + } else { + currentLead += remainingAlignContentDim / 2; + } + break; + case YGAlignSpaceBetween: + if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) { + crossDimLead = remainingAlignContentDim / (lineCount - 1); + } + break; + case YGAlignAuto: + case YGAlignFlexStart: + case YGAlignBaseline: + break; + } + } + uint32_t endIndex = 0; + for (uint32_t i = 0; i < lineCount; i++) { + const uint32_t startIndex = endIndex; + uint32_t ii; + + // compute the line's height and find the endIndex + float lineHeight = 0; + float maxAscentForCurrentLine = 0; + float maxDescentForCurrentLine = 0; + for (ii = startIndex; ii < childCount; ii++) { + const YGNodeRef child = node->getChild(ii); + if (child->getStyle().display() == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType() == YGPositionTypeRelative) { + if (child->getLineIndex() != i) { + break; + } + if (YGNodeIsLayoutDimDefined(child, crossAxis)) { + lineHeight = YGFloatMax( + lineHeight, + child->getLayout().measuredDimensions[dim[crossAxis]] + + child->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap()); + } + if (YGNodeAlignItem(node, child) == YGAlignBaseline) { + const float ascent = YGBaseline(child, layoutContext) + + child + ->getLeadingMargin( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap(); + const float descent = + child->getLayout().measuredDimensions[YGDimensionHeight] + + child + ->getMarginForAxis( + YGFlexDirectionColumn, availableInnerWidth) + .unwrap() - + ascent; + maxAscentForCurrentLine = + YGFloatMax(maxAscentForCurrentLine, ascent); + maxDescentForCurrentLine = + YGFloatMax(maxDescentForCurrentLine, descent); + lineHeight = YGFloatMax( + lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine); + } + } + } + endIndex = ii; + lineHeight += crossDimLead; + + if (performLayout) { + for (ii = startIndex; ii < endIndex; ii++) { + const YGNodeRef child = node->getChild(ii); + if (child->getStyle().display() == YGDisplayNone) { + continue; + } + if (child->getStyle().positionType() == YGPositionTypeRelative) { + switch (YGNodeAlignItem(node, child)) { + case YGAlignFlexStart: { + child->setLayoutPosition( + currentLead + + child->getLeadingMargin(crossAxis, availableInnerWidth) + .unwrap(), + pos[crossAxis]); + break; + } + case YGAlignFlexEnd: { + child->setLayoutPosition( + currentLead + lineHeight - + child->getTrailingMargin(crossAxis, availableInnerWidth) + .unwrap() - + child->getLayout().measuredDimensions[dim[crossAxis]], + pos[crossAxis]); + break; + } + case YGAlignCenter: { + float childHeight = + child->getLayout().measuredDimensions[dim[crossAxis]]; + + child->setLayoutPosition( + currentLead + (lineHeight - childHeight) / 2, + pos[crossAxis]); + break; + } + case YGAlignStretch: { + child->setLayoutPosition( + currentLead + + child->getLeadingMargin(crossAxis, availableInnerWidth) + .unwrap(), + pos[crossAxis]); + + // Remeasure child with the line height as it as been only + // measured with the owners height yet. + if (!YGNodeIsStyleDimDefined( + child, crossAxis, availableInnerCrossDim)) { + const float childWidth = isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionWidth] + + child->getMarginForAxis(mainAxis, availableInnerWidth) + .unwrap()) + : lineHeight; + + const float childHeight = !isMainAxisRow + ? (child->getLayout() + .measuredDimensions[YGDimensionHeight] + + child->getMarginForAxis(crossAxis, availableInnerWidth) + .unwrap()) + : lineHeight; + + if (!(YGFloatsEqual( + childWidth, + child->getLayout() + .measuredDimensions[YGDimensionWidth]) && + YGFloatsEqual( + childHeight, + child->getLayout() + .measuredDimensions[YGDimensionHeight]))) { + YGLayoutNodeInternal( + child, + childWidth, + childHeight, + direction, + YGMeasureModeExactly, + YGMeasureModeExactly, + availableInnerWidth, + availableInnerHeight, + true, + 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; + } + } + + // STEP 9: COMPUTING FINAL DIMENSIONS + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionRow, + availableWidth - marginAxisRow, + ownerWidth, + ownerWidth), + YGDimensionWidth); + + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + YGFlexDirectionColumn, + availableHeight - marginAxisColumn, + ownerHeight, + ownerWidth), + YGDimensionHeight); + + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == YGMeasureModeUndefined || + (node->getStyle().overflow() != YGOverflowScroll && + measureModeMainDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth), + dim[mainAxis]); + + } else if ( + measureModeMainDim == YGMeasureModeAtMost && + node->getStyle().overflow() == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + YGFloatMax( + YGFloatMin( + availableInnerMainDim + paddingAndBorderAxisMain, + YGNodeBoundAxisWithinMinAndMax( + node, + mainAxis, + YGFloatOptional{maxLineMainDim}, + mainAxisownerSize) + .unwrap()), + paddingAndBorderAxisMain), + dim[mainAxis]); + } + + if (measureModeCrossDim == YGMeasureModeUndefined || + (node->getStyle().overflow() != YGOverflowScroll && + measureModeCrossDim == YGMeasureModeAtMost)) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->setLayoutMeasuredDimension( + YGNodeBoundAxis( + node, + crossAxis, + totalLineCrossDim + paddingAndBorderAxisCross, + crossAxisownerSize, + ownerWidth), + dim[crossAxis]); + + } else if ( + measureModeCrossDim == YGMeasureModeAtMost && + node->getStyle().overflow() == YGOverflowScroll) { + node->setLayoutMeasuredDimension( + YGFloatMax( + YGFloatMin( + availableInnerCrossDim + paddingAndBorderAxisCross, + YGNodeBoundAxisWithinMinAndMax( + node, + crossAxis, + YGFloatOptional{totalLineCrossDim + + paddingAndBorderAxisCross}, + crossAxisownerSize) + .unwrap()), + paddingAndBorderAxisCross), + dim[crossAxis]); + } + + // As we only wrapped in normal direction yet, we need to reverse the + // positions on wrap-reverse. + if (performLayout && node->getStyle().flexWrap() == YGWrapWrapReverse) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = YGNodeGetChild(node, i); + if (child->getStyle().positionType() == YGPositionTypeRelative) { + child->setLayoutPosition( + node->getLayout().measuredDimensions[dim[crossAxis]] - + child->getLayout().position[pos[crossAxis]] - + child->getLayout().measuredDimensions[dim[crossAxis]], + pos[crossAxis]); + } + } + } + + if (performLayout) { + // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN + for (auto child : node->getChildren()) { + if (child->getStyle().positionType() != YGPositionTypeAbsolute) { + continue; + } + YGNodeAbsoluteLayoutChild( + node, + child, + availableInnerWidth, + isMainAxisRow ? measureModeMainDim : measureModeCrossDim, + availableInnerHeight, + direction, + config, + layoutMarkerData, + layoutContext, + depth, + generationCount); + } + + // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN + const bool needsMainTrailingPos = mainAxis == YGFlexDirectionRowReverse || + mainAxis == YGFlexDirectionColumnReverse; + const bool needsCrossTrailingPos = crossAxis == YGFlexDirectionRowReverse || + crossAxis == YGFlexDirectionColumnReverse; + + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (uint32_t i = 0; i < childCount; i++) { + const YGNodeRef child = node->getChild(i); + if (child->getStyle().display() == YGDisplayNone) { + continue; + } + if (needsMainTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, mainAxis); + } + + if (needsCrossTrailingPos) { + YGNodeSetChildTrailingPosition(node, child, crossAxis); + } + } + } + } +} + +bool gPrintChanges = false; +bool gPrintSkips = false; + +static const char* spacer = + " "; + +static const char* YGSpacer(const unsigned long level) { + const size_t spacerLen = strlen(spacer); + if (level > spacerLen) { + return &spacer[0]; + } else { + return &spacer[spacerLen - level]; + } +} + +static const char* YGMeasureModeName( + const YGMeasureMode mode, + const bool performLayout) { + constexpr auto N = enums::count(); + const char* kMeasureModeNames[N] = {"UNDEFINED", "EXACTLY", "AT_MOST"}; + const char* kLayoutModeNames[N] = { + "LAY_UNDEFINED", "LAY_EXACTLY", "LAY_AT_MOST"}; + + if (mode >= N) { + return ""; + } + + return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode]; +} + +static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize( + YGMeasureMode sizeMode, + float size, + float lastComputedSize) { + return sizeMode == YGMeasureModeExactly && + YGFloatsEqual(size, lastComputedSize); +} + +static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits( + YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastComputedSize) { + return sizeMode == YGMeasureModeAtMost && + lastSizeMode == YGMeasureModeUndefined && + (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize)); +} + +static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + YGMeasureMode sizeMode, + float size, + YGMeasureMode lastSizeMode, + float lastSize, + float lastComputedSize) { + return lastSizeMode == YGMeasureModeAtMost && + sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) && + !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) && + lastSize > size && + (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize)); +} + +YOGA_EXPORT float YGRoundValueToPixelGrid( + const float value, + const float pointScaleFactor, + const bool forceCeil, + const bool forceFloor) { + double scaledValue = ((double) value) * pointScaleFactor; + // We want to calculate `fractial` such that `floor(scaledValue) = scaledValue + // - fractial`. + float fractial = fmodf(scaledValue, 1.0f); + if (fractial < 0) { + // This branch is for handling negative numbers for `value`. + // + // Regarding `floor` and `ceil`. Note that for a number x, `floor(x) <= x <= + // ceil(x)` even for negative numbers. Here are a couple of examples: + // - x = 2.2: floor( 2.2) = 2, ceil( 2.2) = 3 + // - x = -2.2: floor(-2.2) = -3, ceil(-2.2) = -2 + // + // Regarding `fmodf`. For fractional negative numbers, `fmodf` returns a + // negative number. For example, `fmodf(-2.2) = -0.2`. However, we want + // `fractial` to be the number such that subtracting it from `value` will + // give us `floor(value)`. In the case of negative numbers, adding 1 to + // `fmodf(value)` gives us this. Let's continue the example from above: + // - fractial = fmodf(-2.2) = -0.2 + // - Add 1 to the fraction: fractial2 = fractial + 1 = -0.2 + 1 = 0.8 + // - Finding the `floor`: -2.2 - fractial2 = -2.2 - 0.8 = -3 + ++fractial; + } + if (YGFloatsEqual(fractial, 0)) { + // First we check if the value is already rounded + scaledValue = scaledValue - fractial; + } else if (YGFloatsEqual(fractial, 1.0f)) { + scaledValue = scaledValue - fractial + 1.0f; + } else if (forceCeil) { + // Next we check if we need to use forced rounding + scaledValue = scaledValue - fractial + 1.0f; + } else if (forceFloor) { + scaledValue = scaledValue - fractial; + } else { + // Finally we just round the value + scaledValue = scaledValue - fractial + + (!YGFloatIsUndefined(fractial) && + (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f)) + ? 1.0f + : 0.0f); + } + return (YGFloatIsUndefined(scaledValue) || + YGFloatIsUndefined(pointScaleFactor)) + ? YGUndefined + : scaledValue / pointScaleFactor; +} + +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->pointScaleFactor != 0; + const float effectiveWidth = useRoundedComparison + ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false) + : width; + const float effectiveHeight = useRoundedComparison + ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false) + : height; + const float effectiveLastWidth = useRoundedComparison + ? YGRoundValueToPixelGrid( + lastWidth, config->pointScaleFactor, false, false) + : lastWidth; + const float effectiveLastHeight = useRoundedComparison + ? YGRoundValueToPixelGrid( + lastHeight, config->pointScaleFactor, false, false) + : lastHeight; + + const bool hasSameWidthSpec = lastWidthMode == widthMode && + YGFloatsEqual(effectiveLastWidth, effectiveWidth); + const bool hasSameHeightSpec = lastHeightMode == heightMode && + YGFloatsEqual(effectiveLastHeight, effectiveHeight); + + const bool widthIsCompatible = + hasSameWidthSpec || + YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize( + widthMode, width - marginRow, lastComputedWidth) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits( + widthMode, width - marginRow, lastWidthMode, lastComputedWidth) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + widthMode, + width - marginRow, + lastWidthMode, + lastWidth, + lastComputedWidth); + + const bool heightIsCompatible = + hasSameHeightSpec || + YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize( + heightMode, height - marginColumn, lastComputedHeight) || + YGMeasureModeOldSizeIsUnspecifiedAndStillFits( + heightMode, + height - marginColumn, + lastHeightMode, + lastComputedHeight) || + YGMeasureModeNewMeasureSizeIsStricterAndStillValid( + heightMode, + height - marginColumn, + lastHeightMode, + lastHeight, + lastComputedHeight); + + return widthIsCompatible && heightIsCompatible; +} + +// +// This is a wrapper around the YGNodelayoutImpl function. It determines whether +// the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as YGNodelayoutImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool YGLayoutNodeInternal( + const YGNodeRef node, + const float availableWidth, + const float availableHeight, + const YGDirection ownerDirection, + const YGMeasureMode widthMeasureMode, + const YGMeasureMode heightMeasureMode, + const float ownerWidth, + const float ownerHeight, + const bool performLayout, + const 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) { + // Invalidate the cached results. + layout->nextCachedMeasurementsIndex = 0; + layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1; + layout->cachedLayout.computedWidth = -1; + layout->cachedLayout.computedHeight = -1; + } + + YGCachedMeasurement* cachedResults = nullptr; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the + // positions and dimensions for nodes in the subtree. The algorithm assumes + // that each node gets layed out a maximum of one time per tree layout, but + // multiple measurements may be required to resolve all of the flex + // dimensions. We handle nodes with measure functions specially here because + // they are the most expensive to measure, so it's worth avoiding redundant + // measurements if at all possible. + if (node->hasMeasureFunc()) { + const float marginAxisRow = + node->getMarginForAxis(YGFlexDirectionRow, ownerWidth).unwrap(); + const float marginAxisColumn = + node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth).unwrap(); + + // First, try to use the layout cache. + if (YGNodeCanUseCachedMeasurement( + widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedLayout.widthMeasureMode, + layout->cachedLayout.availableWidth, + layout->cachedLayout.heightMeasureMode, + layout->cachedLayout.availableHeight, + layout->cachedLayout.computedWidth, + layout->cachedLayout.computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedLayout; + } else { + // Try to use the measurement cache. + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGNodeCanUseCachedMeasurement( + widthMeasureMode, + availableWidth, + heightMeasureMode, + availableHeight, + layout->cachedMeasurements[i].widthMeasureMode, + layout->cachedMeasurements[i].availableWidth, + layout->cachedMeasurements[i].heightMeasureMode, + layout->cachedMeasurements[i].availableHeight, + layout->cachedMeasurements[i].computedWidth, + layout->cachedMeasurements[i].computedHeight, + marginAxisRow, + marginAxisColumn, + config)) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { + if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) && + YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) && + layout->cachedLayout.widthMeasureMode == widthMeasureMode && + layout->cachedLayout.heightMeasureMode == heightMeasureMode) { + cachedResults = &layout->cachedLayout; + } + } else { + for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) { + if (YGFloatsEqual( + layout->cachedMeasurements[i].availableWidth, availableWidth) && + YGFloatsEqual( + layout->cachedMeasurements[i].availableHeight, availableHeight) && + layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout->cachedMeasurements[i].heightMeasureMode == + heightMeasureMode) { + cachedResults = &layout->cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != nullptr) { + layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth; + layout->measuredDimensions[YGDimensionHeight] = + cachedResults->computedHeight; + + (performLayout ? layoutMarkerData.cachedLayouts + : layoutMarkerData.cachedMeasures) += 1; + + if (gPrintChanges && gPrintSkips) { + Log::log( + node, + YGLogLevelVerbose, + nullptr, + "%s%d.{[skipped] ", + YGSpacer(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) { + // Use the single layout cache entry. + newCacheEntry = &layout->cachedLayout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = + &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex]; + layout->nextCachedMeasurementsIndex++; + } + + newCacheEntry->availableWidth = availableWidth; + newCacheEntry->availableHeight = availableHeight; + newCacheEntry->widthMeasureMode = widthMeasureMode; + newCacheEntry->heightMeasureMode = heightMeasureMode; + newCacheEntry->computedWidth = + layout->measuredDimensions[YGDimensionWidth]; + newCacheEntry->computedHeight = + layout->measuredDimensions[YGDimensionHeight]; + } + } + + if (performLayout) { + node->setLayoutDimension( + node->getLayout().measuredDimensions[YGDimensionWidth], + YGDimensionWidth); + node->setLayoutDimension( + node->getLayout().measuredDimensions[YGDimensionHeight], + YGDimensionHeight); + + node->setHasNewLayout(true); + node->setDirty(false); + } + + 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(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"); + + // We store points for Pixel as we will use it for rounding + if (pixelsInPoint == 0.0f) { + // Zero is used to skip rounding + config->pointScaleFactor = 0.0f; + } else { + config->pointScaleFactor = pixelsInPoint; + } +} + +static void YGRoundToPixelGrid( + const YGNodeRef node, + const float pointScaleFactor, + const float absoluteLeft, + const float absoluteTop) { + if (pointScaleFactor == 0.0f) { + return; + } + + const float nodeLeft = node->getLayout().position[YGEdgeLeft]; + const float nodeTop = node->getLayout().position[YGEdgeTop]; + + const float nodeWidth = node->getLayout().dimensions[YGDimensionWidth]; + const float nodeHeight = node->getLayout().dimensions[YGDimensionHeight]; + + const float absoluteNodeLeft = absoluteLeft + nodeLeft; + const float absoluteNodeTop = absoluteTop + nodeTop; + + const float absoluteNodeRight = absoluteNodeLeft + nodeWidth; + const float absoluteNodeBottom = absoluteNodeTop + nodeHeight; + + // If a node has a custom measure function we never want to round down its + // size as this could lead to unwanted text truncation. + const bool textRounding = node->getNodeType() == YGNodeTypeText; + + node->setLayoutPosition( + YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding), + YGEdgeLeft); + + node->setLayoutPosition( + YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding), + YGEdgeTop); + + // We multiply dimension by scale factor and if the result is close to the + // whole number, we don't have any fraction To verify if the result is close + // to whole number we want to check both floor and ceil numbers + const bool hasFractionalWidth = + !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0); + const bool hasFractionalHeight = + !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) && + !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0); + + node->setLayoutDimension( + YGRoundValueToPixelGrid( + absoluteNodeRight, + pointScaleFactor, + (textRounding && hasFractionalWidth), + (textRounding && !hasFractionalWidth)) - + YGRoundValueToPixelGrid( + absoluteNodeLeft, pointScaleFactor, false, textRounding), + YGDimensionWidth); + + node->setLayoutDimension( + YGRoundValueToPixelGrid( + absoluteNodeBottom, + pointScaleFactor, + (textRounding && hasFractionalHeight), + (textRounding && !hasFractionalHeight)) - + YGRoundValueToPixelGrid( + absoluteNodeTop, pointScaleFactor, false, textRounding), + YGDimensionHeight); + + const uint32_t childCount = YGNodeGetChildCount(node); + for (uint32_t i = 0; i < childCount; i++) { + YGRoundToPixelGrid( + YGNodeGetChild(node, i), + pointScaleFactor, + absoluteNodeLeft, + absoluteNodeTop); + } +} + +static void unsetUseLegacyFlagRecursively(YGNodeRef node) { + node->getConfig()->useLegacyStretchBehaviour = false; + for (auto child : node->getChildren()) { + unsetUseLegacyFlagRecursively(child); + } +} + +YOGA_EXPORT void YGNodeCalculateLayoutWithContext( + const YGNodeRef node, + const float ownerWidth, + const float ownerHeight, + const YGDirection ownerDirection, + void* layoutContext) { + + Event::publish(node, {layoutContext}); + LayoutData markerData = {}; + + // Increment the generation count. This will force the recursive routine to + // visit all dirty nodes at least once. Subsequent visits will be skipped if + // the input parameters don't change. + gCurrentGenerationCount.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, // tree root + gCurrentGenerationCount.load(std::memory_order_relaxed))) { + node->setPosition( + node->getLayout().direction(), ownerWidth, ownerHeight, ownerWidth); + YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f); + +#ifdef DEBUG + if (node->getConfig()->printTree) { + YGNodePrint( + node, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } +#endif + } + + Event::publish(node, {layoutContext, &markerData}); + + // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we + // aren't sure whether client's of yoga have gotten rid off this flag or not. + // So logging this in YGLayout would help to find out the call sites depending + // on this flag. This check would be removed once we are sure no one is + // dependent on this flag anymore. The flag + // `shouldDiffLayoutWithoutLegacyStretchBehaviour` in YGConfig will help to + // run experiments. + if (node->getConfig()->shouldDiffLayoutWithoutLegacyStretchBehaviour && + node->didUseLegacyFlag()) { + const YGNodeRef nodeWithoutLegacyFlag = YGNodeDeepClone(node); + nodeWithoutLegacyFlag->resolveDimension(); + // Recursively mark nodes as dirty + nodeWithoutLegacyFlag->markDirtyAndPropogateDownwards(); + gCurrentGenerationCount.fetch_add(1, std::memory_order_relaxed); + // Rerun the layout, and calculate the diff + unsetUseLegacyFlagRecursively(nodeWithoutLegacyFlag); + LayoutData layoutMarkerData = {}; + if (YGLayoutNodeInternal( + nodeWithoutLegacyFlag, + width, + height, + ownerDirection, + widthMeasureMode, + heightMeasureMode, + ownerWidth, + ownerHeight, + true, + LayoutPassReason::kInitial, + nodeWithoutLegacyFlag->getConfig(), + layoutMarkerData, + layoutContext, + 0, // tree root + gCurrentGenerationCount.load(std::memory_order_relaxed))) { + nodeWithoutLegacyFlag->setPosition( + nodeWithoutLegacyFlag->getLayout().direction(), + ownerWidth, + ownerHeight, + ownerWidth); + YGRoundToPixelGrid( + nodeWithoutLegacyFlag, + nodeWithoutLegacyFlag->getConfig()->pointScaleFactor, + 0.0f, + 0.0f); + + // Set whether the two layouts are different or not. + auto neededLegacyStretchBehaviour = + !nodeWithoutLegacyFlag->isLayoutTreeEqualToNode(*node); + node->setLayoutDoesLegacyFlagAffectsLayout(neededLegacyStretchBehaviour); + +#ifdef DEBUG + if (nodeWithoutLegacyFlag->getConfig()->printTree) { + YGNodePrint( + nodeWithoutLegacyFlag, + (YGPrintOptions)( + YGPrintOptionsLayout | YGPrintOptionsChildren | + YGPrintOptionsStyle)); + } +#endif + } + YGConfigFreeRecursive(nodeWithoutLegacyFlag); + YGNodeFreeRecursive(nodeWithoutLegacyFlag); + } +} + +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 + } +} + +YOGA_EXPORT void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour( + const YGConfigRef config, + const bool shouldDiffLayout) { + config->shouldDiffLayoutWithoutLegacyStretchBehaviour = shouldDiffLayout; +} + +void YGAssert(const bool condition, const char* message) { + if (!condition) { + Log::log(YGNodeRef{nullptr}, YGLogLevelFatal, nullptr, "%s\n", message); + } +} + +void YGAssertWithNode( + const YGNodeRef node, + const bool condition, + const char* message) { + if (!condition) { + Log::log(node, YGLogLevelFatal, nullptr, "%s\n", message); + } +} + +void YGAssertWithConfig( + const YGConfigRef config, + const bool condition, + const char* message) { + if (!condition) { + Log::log(config, YGLogLevelFatal, nullptr, "%s\n", message); + } +} + +YOGA_EXPORT void YGConfigSetExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature, + const bool enabled) { + config->experimentalFeatures[feature] = enabled; +} + +inline bool YGConfigIsExperimentalFeatureEnabled( + const YGConfigRef config, + const YGExperimentalFeature feature) { + return config->experimentalFeatures[feature]; +} + +YOGA_EXPORT void YGConfigSetUseWebDefaults( + const YGConfigRef config, + const bool enabled) { + config->useWebDefaults = enabled; +} + +YOGA_EXPORT void YGConfigSetUseLegacyStretchBehaviour( + const YGConfigRef config, + const bool useLegacyStretchBehaviour) { + config->useLegacyStretchBehaviour = useLegacyStretchBehaviour; +} + +bool YGConfigGetUseWebDefaults(const YGConfigRef config) { + return config->useWebDefaults; +} + +YOGA_EXPORT void YGConfigSetContext(const YGConfigRef config, void* context) { + config->context = context; +} + +YOGA_EXPORT void* YGConfigGetContext(const YGConfigRef config) { + return config->context; +} + +YOGA_EXPORT void YGConfigSetCloneNodeFunc( + const YGConfigRef config, + const YGCloneNodeFunc callback) { + config->setCloneNodeCallback(callback); +} + +static void YGTraverseChildrenPreOrder( + const YGVector& children, + const std::function& f) { + for (YGNodeRef node : children) { + f(node); + YGTraverseChildrenPreOrder(node->getChildren(), f); + } +} + +void YGTraversePreOrder( + YGNodeRef const node, + std::function&& f) { + if (!node) { + return; + } + f(node); + YGTraverseChildrenPreOrder(node->getChildren(), f); +} diff --git a/doric-Qt/doric/yoga/Yoga.h b/doric-Qt/doric/yoga/Yoga.h new file mode 100644 index 00000000..68ed0a62 --- /dev/null +++ b/doric-Qt/doric/yoga/Yoga.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifndef __cplusplus +#include +#endif + +#include "YGEnums.h" +#include "YGMacros.h" +#include "YGValue.h" + +YG_EXTERN_C_BEGIN + +typedef struct YGSize { + float width; + float height; +} YGSize; + +typedef struct YGConfig* YGConfigRef; + +typedef struct YGNode* YGNodeRef; +typedef const struct YGNode* YGNodeConstRef; + +typedef YGSize (*YGMeasureFunc)( + YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode); +typedef float (*YGBaselineFunc)(YGNodeRef node, float width, float height); +typedef void (*YGDirtiedFunc)(YGNodeRef node); +typedef void (*YGPrintFunc)(YGNodeRef node); +typedef void (*YGNodeCleanupFunc)(YGNodeRef node); +typedef int (*YGLogger)( + YGConfigRef config, + YGNodeRef node, + YGLogLevel level, + const char* format, + va_list args); +typedef YGNodeRef ( + *YGCloneNodeFunc)(YGNodeRef oldNode, YGNodeRef owner, int childIndex); + +// YGNode +WIN_EXPORT YGNodeRef YGNodeNew(void); +WIN_EXPORT YGNodeRef YGNodeNewWithConfig(YGConfigRef config); +WIN_EXPORT YGNodeRef YGNodeClone(YGNodeRef node); +WIN_EXPORT void YGNodeFree(YGNodeRef node); +WIN_EXPORT void YGNodeFreeRecursiveWithCleanupFunc( + YGNodeRef node, + YGNodeCleanupFunc cleanup); +WIN_EXPORT void YGNodeFreeRecursive(YGNodeRef node); +WIN_EXPORT void YGNodeReset(YGNodeRef node); + +WIN_EXPORT void YGNodeInsertChild( + YGNodeRef node, + YGNodeRef child, + uint32_t index); + +WIN_EXPORT void YGNodeRemoveChild(YGNodeRef node, YGNodeRef child); +WIN_EXPORT void YGNodeRemoveAllChildren(YGNodeRef node); +WIN_EXPORT YGNodeRef YGNodeGetChild(YGNodeRef node, uint32_t index); +WIN_EXPORT YGNodeRef YGNodeGetOwner(YGNodeRef node); +WIN_EXPORT YGNodeRef YGNodeGetParent(YGNodeRef node); +WIN_EXPORT uint32_t YGNodeGetChildCount(YGNodeRef node); +WIN_EXPORT void YGNodeSetChildren( + YGNodeRef owner, + const YGNodeRef children[], + uint32_t count); + +WIN_EXPORT void YGNodeSetIsReferenceBaseline( + YGNodeRef node, + bool isReferenceBaseline); + +WIN_EXPORT bool YGNodeIsReferenceBaseline(YGNodeRef node); + +WIN_EXPORT void YGNodeCalculateLayout( + YGNodeRef node, + float availableWidth, + float availableHeight, + YGDirection ownerDirection); + +// Mark a node as dirty. Only valid for nodes with a custom measure function +// set. +// +// Yoga knows when to mark all other nodes as dirty but because nodes with +// measure functions depend on information not known to Yoga they must perform +// this dirty marking manually. +WIN_EXPORT void YGNodeMarkDirty(YGNodeRef node); + +// Marks the current node and all its descendants as dirty. +// +// Intended to be used for Uoga benchmarks. Don't use in production, as calling +// `YGCalculateLayout` will cause the recalculation of each and every node. +WIN_EXPORT void YGNodeMarkDirtyAndPropogateToDescendants(YGNodeRef node); + +WIN_EXPORT void YGNodePrint(YGNodeRef node, YGPrintOptions options); + +WIN_EXPORT bool YGFloatIsUndefined(float value); + +WIN_EXPORT bool YGNodeCanUseCachedMeasurement( + YGMeasureMode widthMode, + float width, + YGMeasureMode heightMode, + float height, + YGMeasureMode lastWidthMode, + float lastWidth, + YGMeasureMode lastHeightMode, + float lastHeight, + float lastComputedWidth, + float lastComputedHeight, + float marginRow, + float marginColumn, + YGConfigRef config); + +WIN_EXPORT void YGNodeCopyStyle(YGNodeRef dstNode, YGNodeRef srcNode); + +WIN_EXPORT void* YGNodeGetContext(YGNodeRef node); +WIN_EXPORT void YGNodeSetContext(YGNodeRef node, void* context); +void YGConfigSetPrintTreeFlag(YGConfigRef config, bool enabled); +bool YGNodeHasMeasureFunc(YGNodeRef node); +WIN_EXPORT void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc); +bool YGNodeHasBaselineFunc(YGNodeRef node); +void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc); +YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node); +void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc); +void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc); +WIN_EXPORT bool YGNodeGetHasNewLayout(YGNodeRef node); +WIN_EXPORT void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout); +YGNodeType YGNodeGetNodeType(YGNodeRef node); +void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType); +WIN_EXPORT bool YGNodeIsDirty(YGNodeRef node); +bool YGNodeLayoutGetDidUseLegacyFlag(YGNodeRef node); + +WIN_EXPORT void YGNodeStyleSetDirection(YGNodeRef node, YGDirection direction); +WIN_EXPORT YGDirection YGNodeStyleGetDirection(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlexDirection( + YGNodeRef node, + YGFlexDirection flexDirection); +WIN_EXPORT YGFlexDirection YGNodeStyleGetFlexDirection(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetJustifyContent( + YGNodeRef node, + YGJustify justifyContent); +WIN_EXPORT YGJustify YGNodeStyleGetJustifyContent(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetAlignContent( + YGNodeRef node, + YGAlign alignContent); +WIN_EXPORT YGAlign YGNodeStyleGetAlignContent(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetAlignItems(YGNodeRef node, YGAlign alignItems); +WIN_EXPORT YGAlign YGNodeStyleGetAlignItems(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetAlignSelf(YGNodeRef node, YGAlign alignSelf); +WIN_EXPORT YGAlign YGNodeStyleGetAlignSelf(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetPositionType( + YGNodeRef node, + YGPositionType positionType); +WIN_EXPORT YGPositionType YGNodeStyleGetPositionType(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlexWrap(YGNodeRef node, YGWrap flexWrap); +WIN_EXPORT YGWrap YGNodeStyleGetFlexWrap(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); +WIN_EXPORT YGOverflow YGNodeStyleGetOverflow(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetDisplay(YGNodeRef node, YGDisplay display); +WIN_EXPORT YGDisplay YGNodeStyleGetDisplay(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlex(YGNodeRef node, float flex); +WIN_EXPORT float YGNodeStyleGetFlex(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlexGrow(YGNodeRef node, float flexGrow); +WIN_EXPORT float YGNodeStyleGetFlexGrow(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlexShrink(YGNodeRef node, float flexShrink); +WIN_EXPORT float YGNodeStyleGetFlexShrink(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetFlexBasis(YGNodeRef node, float flexBasis); +WIN_EXPORT void YGNodeStyleSetFlexBasisPercent(YGNodeRef node, float flexBasis); +WIN_EXPORT void YGNodeStyleSetFlexBasisAuto(YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetFlexBasis(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetPosition( + YGNodeRef node, + YGEdge edge, + float position); +WIN_EXPORT void YGNodeStyleSetPositionPercent( + YGNodeRef node, + YGEdge edge, + float position); +WIN_EXPORT YGValue YGNodeStyleGetPosition(YGNodeConstRef node, YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetMargin(YGNodeRef node, YGEdge edge, float margin); +WIN_EXPORT void YGNodeStyleSetMarginPercent( + YGNodeRef node, + YGEdge edge, + float margin); +WIN_EXPORT void YGNodeStyleSetMarginAuto(YGNodeRef node, YGEdge edge); +WIN_EXPORT YGValue YGNodeStyleGetMargin(YGNodeConstRef node, YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetPadding( + YGNodeRef node, + YGEdge edge, + float padding); +WIN_EXPORT void YGNodeStyleSetPaddingPercent( + YGNodeRef node, + YGEdge edge, + float padding); +WIN_EXPORT YGValue YGNodeStyleGetPadding(YGNodeConstRef node, YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetBorder(YGNodeRef node, YGEdge edge, float border); +WIN_EXPORT float YGNodeStyleGetBorder(YGNodeConstRef node, YGEdge edge); + +WIN_EXPORT void YGNodeStyleSetWidth(YGNodeRef node, float width); +WIN_EXPORT void YGNodeStyleSetWidthPercent(YGNodeRef node, float width); +WIN_EXPORT void YGNodeStyleSetWidthAuto(YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetWidth(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetHeight(YGNodeRef node, float height); +WIN_EXPORT void YGNodeStyleSetHeightPercent(YGNodeRef node, float height); +WIN_EXPORT void YGNodeStyleSetHeightAuto(YGNodeRef node); +WIN_EXPORT YGValue YGNodeStyleGetHeight(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetMinWidth(YGNodeRef node, float minWidth); +WIN_EXPORT void YGNodeStyleSetMinWidthPercent(YGNodeRef node, float minWidth); +WIN_EXPORT YGValue YGNodeStyleGetMinWidth(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetMinHeight(YGNodeRef node, float minHeight); +WIN_EXPORT void YGNodeStyleSetMinHeightPercent(YGNodeRef node, float minHeight); +WIN_EXPORT YGValue YGNodeStyleGetMinHeight(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetMaxWidth(YGNodeRef node, float maxWidth); +WIN_EXPORT void YGNodeStyleSetMaxWidthPercent(YGNodeRef node, float maxWidth); +WIN_EXPORT YGValue YGNodeStyleGetMaxWidth(YGNodeConstRef node); + +WIN_EXPORT void YGNodeStyleSetMaxHeight(YGNodeRef node, float maxHeight); +WIN_EXPORT void YGNodeStyleSetMaxHeightPercent(YGNodeRef node, float maxHeight); +WIN_EXPORT YGValue YGNodeStyleGetMaxHeight(YGNodeConstRef node); + +// Yoga specific properties, not compatible with flexbox specification Aspect +// ratio control the size of the undefined dimension of a node. Aspect ratio is +// encoded as a floating point value width/height. e.g. A value of 2 leads to a +// node with a width twice the size of its height while a value of 0.5 gives the +// opposite effect. +// +// - On a node with a set width/height aspect ratio control the size of the +// unset dimension +// - On a node with a set flex basis aspect ratio controls the size of the node +// in the cross axis if unset +// - On a node with a measure function aspect ratio works as though the measure +// function measures the flex basis +// - On a node with flex grow/shrink aspect ratio controls the size of the node +// in the cross axis if unset +// - Aspect ratio takes min/max dimensions into account +WIN_EXPORT void YGNodeStyleSetAspectRatio(YGNodeRef node, float aspectRatio); +WIN_EXPORT float YGNodeStyleGetAspectRatio(YGNodeConstRef node); + +WIN_EXPORT float YGNodeLayoutGetLeft(YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetTop(YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetRight(YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetBottom(YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetWidth(YGNodeRef node); +WIN_EXPORT float YGNodeLayoutGetHeight(YGNodeRef node); +WIN_EXPORT YGDirection YGNodeLayoutGetDirection(YGNodeRef node); +WIN_EXPORT bool YGNodeLayoutGetHadOverflow(YGNodeRef node); +bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(YGNodeRef node); + +// Get the computed values for these nodes after performing layout. If they were +// set using point values then the returned value will be the same as +// YGNodeStyleGetXXX. However if they were set using a percentage value then the +// returned value is the computed value used during layout. +WIN_EXPORT float YGNodeLayoutGetMargin(YGNodeRef node, YGEdge edge); +WIN_EXPORT float YGNodeLayoutGetBorder(YGNodeRef node, YGEdge edge); +WIN_EXPORT float YGNodeLayoutGetPadding(YGNodeRef node, YGEdge edge); + +WIN_EXPORT void YGConfigSetLogger(YGConfigRef config, YGLogger logger); +WIN_EXPORT void YGAssert(bool condition, const char* message); +WIN_EXPORT void YGAssertWithNode( + YGNodeRef node, + bool condition, + const char* message); +WIN_EXPORT void YGAssertWithConfig( + YGConfigRef config, + bool condition, + const char* message); +// Set this to number of pixels in 1 point to round calculation results If you +// want to avoid rounding - set PointScaleFactor to 0 +WIN_EXPORT void YGConfigSetPointScaleFactor( + YGConfigRef config, + float pixelsInPoint); +void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour( + YGConfigRef config, + bool shouldDiffLayout); + +// Yoga previously had an error where containers would take the maximum space +// possible instead of the minimum like they are supposed to. In practice this +// resulted in implicit behaviour similar to align-self: stretch; Because this +// was such a long-standing bug we must allow legacy users to switch back to +// this behaviour. +WIN_EXPORT void YGConfigSetUseLegacyStretchBehaviour( + YGConfigRef config, + bool useLegacyStretchBehaviour); + +// YGConfig +WIN_EXPORT YGConfigRef YGConfigNew(void); +WIN_EXPORT void YGConfigFree(YGConfigRef config); +WIN_EXPORT void YGConfigCopy(YGConfigRef dest, YGConfigRef src); +WIN_EXPORT int32_t YGConfigGetInstanceCount(void); + +WIN_EXPORT void YGConfigSetExperimentalFeatureEnabled( + YGConfigRef config, + YGExperimentalFeature feature, + bool enabled); +WIN_EXPORT bool YGConfigIsExperimentalFeatureEnabled( + YGConfigRef config, + YGExperimentalFeature feature); + +// Using the web defaults is the preferred configuration for new projects. Usage +// of non web defaults should be considered as legacy. +WIN_EXPORT void YGConfigSetUseWebDefaults(YGConfigRef config, bool enabled); +WIN_EXPORT bool YGConfigGetUseWebDefaults(YGConfigRef config); + +WIN_EXPORT void YGConfigSetCloneNodeFunc( + YGConfigRef config, + YGCloneNodeFunc callback); + +// Export only for C# +WIN_EXPORT YGConfigRef YGConfigGetDefault(void); + +WIN_EXPORT void YGConfigSetContext(YGConfigRef config, void* context); +WIN_EXPORT void* YGConfigGetContext(YGConfigRef config); + +WIN_EXPORT float YGRoundValueToPixelGrid( + float value, + float pointScaleFactor, + bool forceCeil, + bool forceFloor); + +YG_EXTERN_C_END + +#ifdef __cplusplus + +#include +#include + +// Calls f on each node in the tree including the given node argument. +void YGTraversePreOrder( + YGNodeRef node, + std::function&& f); + +void YGNodeSetChildren(YGNodeRef owner, const std::vector& children); + +#endif diff --git a/doric-Qt/doric/yoga/event/event.cpp b/doric-Qt/doric/yoga/event/event.cpp new file mode 100644 index 00000000..2b07e381 --- /dev/null +++ b/doric-Qt/doric/yoga/event/event.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "event.h" +#include +#include +#include + +namespace facebook { +namespace yoga { + +const char* LayoutPassReasonToString(const LayoutPassReason value) { + switch (value) { + case LayoutPassReason::kInitial: + return "initial"; + case LayoutPassReason::kAbsLayout: + return "abs_layout"; + case LayoutPassReason::kStretch: + return "stretch"; + case LayoutPassReason::kMultilineStretch: + return "multiline_stretch"; + case LayoutPassReason::kFlexLayout: + return "flex_layout"; + case LayoutPassReason::kMeasureChild: + return "measure"; + case LayoutPassReason::kAbsMeasureChild: + return "abs_measure"; + case LayoutPassReason::kFlexMeasure: + return "flex_measure"; + default: + return "unknown"; + } +} + +namespace { + +struct Node { + std::function subscriber = nullptr; + Node* next = nullptr; + + Node(std::function&& subscriber) + : subscriber{std::move(subscriber)} {} +}; + +std::atomic subscribers{nullptr}; + +Node* push(Node* newHead) { + Node* oldHead; + do { + oldHead = subscribers.load(std::memory_order_relaxed); + if (newHead != nullptr) { + newHead->next = oldHead; + } + } while (!subscribers.compare_exchange_weak( + oldHead, newHead, std::memory_order_release, std::memory_order_relaxed)); + return oldHead; +} + +} // namespace + +void Event::reset() { + auto head = push(nullptr); + while (head != nullptr) { + auto current = head; + head = head->next; + delete current; + } +} + +void Event::subscribe(std::function&& subscriber) { + push(new Node{std::move(subscriber)}); +} + +void Event::publish(const YGNode& node, Type eventType, const Data& eventData) { + for (auto subscriber = subscribers.load(std::memory_order_relaxed); + subscriber != nullptr; + subscriber = subscriber->next) { + subscriber->subscriber(node, eventType, eventData); + } +} + +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/event/event.h b/doric-Qt/doric/yoga/event/event.h new file mode 100644 index 00000000..309dacb5 --- /dev/null +++ b/doric-Qt/doric/yoga/event/event.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include + +struct YGConfig; +struct YGNode; + +namespace facebook { +namespace yoga { + +enum struct LayoutType : int { + kLayout = 0, + kMeasure = 1, + kCachedLayout = 2, + kCachedMeasure = 3 +}; + +enum struct LayoutPassReason : int { + kInitial = 0, + kAbsLayout = 1, + kStretch = 2, + kMultilineStretch = 3, + kFlexLayout = 4, + kMeasureChild = 5, + kAbsMeasureChild = 6, + kFlexMeasure = 7, + COUNT +}; + +struct LayoutData { + int layouts; + int measures; + int maxMeasureCache; + int cachedLayouts; + int cachedMeasures; + int measureCallbacks; + std::array(LayoutPassReason::COUNT)> + measureCallbackReasonsCount; +}; + +const char* LayoutPassReasonToString(const LayoutPassReason value); + +struct YOGA_EXPORT Event { + enum Type { + NodeAllocation, + NodeDeallocation, + NodeLayout, + LayoutPassStart, + LayoutPassEnd, + MeasureCallbackStart, + MeasureCallbackEnd, + NodeBaselineStart, + NodeBaselineEnd, + }; + class Data; + using Subscriber = void(const YGNode&, Type, Data); + using Subscribers = std::vector>; + + template + struct TypedData {}; + + class Data { + const void* data_; + + public: + template + Data(const TypedData& data) : data_{&data} {} + + template + const TypedData& get() const { + return *static_cast*>(data_); + }; + }; + + static void reset(); + + static void subscribe(std::function&& subscriber); + + template + static void publish(const YGNode& node, const TypedData& eventData = {}) { +#ifdef YG_ENABLE_EVENTS + publish(node, E, Data{eventData}); +#endif + } + + template + static void publish(const YGNode* node, const TypedData& eventData = {}) { + publish(*node, eventData); + } + +private: + static void publish(const YGNode&, Type, const Data&); +}; + +template <> +struct Event::TypedData { + YGConfig* config; +}; + +template <> +struct Event::TypedData { + YGConfig* config; +}; + +template <> +struct Event::TypedData { + void* layoutContext; +}; + +template <> +struct Event::TypedData { + void* layoutContext; + LayoutData* layoutData; +}; + +template <> +struct Event::TypedData { + void* layoutContext; + float width; + YGMeasureMode widthMeasureMode; + float height; + YGMeasureMode heightMeasureMode; + float measuredWidth; + float measuredHeight; + const LayoutPassReason reason; +}; + +template <> +struct Event::TypedData { + LayoutType layoutType; + void* layoutContext; +}; + +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/internal/experiments-inl.h b/doric-Qt/doric/yoga/internal/experiments-inl.h new file mode 100644 index 00000000..959d9c33 --- /dev/null +++ b/doric-Qt/doric/yoga/internal/experiments-inl.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "experiments.h" + +#include + +namespace facebook { +namespace yoga { +namespace internal { + +namespace detail { +extern std::bitset enabledExperiments; +} // namespace detail + +inline bool isEnabled(Experiment experiment) { + return detail::enabledExperiments.test(static_cast(experiment)); +} + +inline void disableAllExperiments() { + detail::enabledExperiments = 0; +} + +} // namespace internal +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/internal/experiments.cpp b/doric-Qt/doric/yoga/internal/experiments.cpp new file mode 100644 index 00000000..16f196d5 --- /dev/null +++ b/doric-Qt/doric/yoga/internal/experiments.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "experiments.h" +#include "experiments-inl.h" + +namespace facebook { +namespace yoga { +namespace internal { + +namespace detail { + +std::bitset enabledExperiments = 0; + +} // namespace detail + +void enable(Experiment experiment) { + detail::enabledExperiments.set(static_cast(experiment)); +} + +void disable(Experiment experiment) { + detail::enabledExperiments.reset(static_cast(experiment)); +} + +bool toggle(Experiment experiment) { + auto bit = static_cast(experiment); + auto previousState = detail::enabledExperiments.test(bit); + detail::enabledExperiments.flip(bit); + return previousState; +} + +} // namespace internal +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/internal/experiments.h b/doric-Qt/doric/yoga/internal/experiments.h new file mode 100644 index 00000000..1bdb7014 --- /dev/null +++ b/doric-Qt/doric/yoga/internal/experiments.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook { +namespace yoga { +namespace internal { + +enum struct Experiment : size_t { + kDoubleMeasureCallbacks, +}; + +void enable(Experiment); +void disable(Experiment); +bool toggle(Experiment); + +} // namespace internal +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/log.cpp b/doric-Qt/doric/yoga/log.cpp new file mode 100644 index 00000000..fe6fbbc6 --- /dev/null +++ b/doric-Qt/doric/yoga/log.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "log.h" + +#include "Yoga.h" +#include "YGConfig.h" +#include "YGNode.h" + +namespace facebook { +namespace yoga { +namespace detail { + +namespace { + +void vlog( + YGConfig* config, + YGNode* node, + YGLogLevel level, + void* context, + const char* format, + va_list args) { + YGConfig* logConfig = config != nullptr ? config : YGConfigGetDefault(); + logConfig->log(logConfig, node, level, context, format, args); + + if (level == YGLogLevelFatal) { + abort(); + } +} +} // namespace + +YOGA_EXPORT void Log::log( + YGNode* node, + YGLogLevel level, + void* context, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog( + node == nullptr ? nullptr : node->getConfig(), + node, + level, + context, + format, + args); + va_end(args); +} + +void Log::log( + YGConfig* config, + YGLogLevel level, + void* context, + const char* format, + ...) noexcept { + va_list args; + va_start(args, format); + vlog(config, nullptr, level, context, format, args); + va_end(args); +} + +} // namespace detail +} // namespace yoga +} // namespace facebook diff --git a/doric-Qt/doric/yoga/log.h b/doric-Qt/doric/yoga/log.h new file mode 100644 index 00000000..ae33744c --- /dev/null +++ b/doric-Qt/doric/yoga/log.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "YGEnums.h" + +struct YGNode; +struct YGConfig; + +namespace facebook { +namespace yoga { + +namespace detail { + +struct Log { + static void log( + YGNode* node, + YGLogLevel level, + void*, + const char* message, + ...) noexcept; + + static void log( + YGConfig* config, + YGLogLevel level, + void*, + const char* format, + ...) noexcept; +}; + +} // namespace detail +} // namespace yoga +} // namespace facebook